[med-svn] [SCM] aghermann branch, master, updated. 551e213a23b59b71cba6a9c3a282d1b60e21b854

andrei zavada jh at johnhommer.com
Sun Apr 21 23:17:56 UTC 2013


The following commit has been merged in the master branch:
commit 86c1ce0c12e75a6a2de7670767e65f097bcc9bd7
Author: andrei zavada <jh at johnhommer.com>
Date:   Sat Apr 13 10:15:00 2013 +0000

    WIP

diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 209e348..86c6ae1 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -7,12 +7,14 @@ noinst_LIBRARIES := \
 
 liba_a_SOURCES := \
 	libcommon.cc \
+	subject_id.cc \
 	config-validate.hh \
 	string.hh \
 	globals.hh \
 	alg.hh \
 	fs.hh \
-	lang.hh
+	lang.hh \
+	subject_id.hh
 
 if DO_PCH
 BUILT_SOURCES := \
@@ -20,7 +22,8 @@ BUILT_SOURCES := \
 	string.hh.gch \
 	globals.hh.gch \
 	alg.hh.gch \
-	fs.hh.gch
+	fs.hh.gch \
+	subject_id.hh
 
 %.hh.gch: %.hh
 	$(CXXCOMPILE) -c $<
diff --git a/src/common/subject_id.cc b/src/common/subject_id.cc
new file mode 100644
index 0000000..21b5ac1
--- /dev/null
+++ b/src/common/subject_id.cc
@@ -0,0 +1,120 @@
+/*
+ *       File name:  common/subject_id.cc
+ *         Project:  Aghermann
+ *          Author:  Andrei Zavada <johnhommer at gmail.com>
+ * Initial version:  2013-04-13
+ *
+ *         Purpose:  subject_id shared between agh::CSubject and libsigfile::CSource
+ *
+ *         License:  GPL
+ */
+
+#include <ctime>
+
+#include <string>
+#include <cstring>
+#include "string.hh"
+#include "subject_id.hh"
+
+#if HAVE_CONFIG_H && !defined(VERSION)
+#  include "config.h"
+#endif
+
+using namespace std;
+using agh::SSubjectId;
+
+SSubjectId::TGender
+SSubjectId::
+char_to_gender( char x)
+{
+	switch ( x ) {
+	case 'M':
+	case 'm':
+		return TGender::male;
+	case 'F':
+	case 'f':
+		return TGender::female;
+	default:
+		return TGender::unknown;
+	}
+}
+
+
+char
+__attribute__ ((const))
+SSubjectId::
+gender_sign( TGender g)
+{
+	switch ( g ) {
+	case TGender::male:
+		return 'M';
+	case TGender::female:
+		return 'F';
+	default:
+		return 'X';
+	}
+}
+
+
+
+namespace {
+
+int str_to_english_month( const string& s)
+{
+	if ( strcasecmp( s.c_str(), "jan") == 0 )
+		return 0;
+	if ( strcasecmp( s.c_str(), "feb") == 0 )
+		return 1;
+	if ( strcasecmp( s.c_str(), "mar") == 0 )
+		return 2;
+	if ( strcasecmp( s.c_str(), "apr") == 0 )
+		return 3;
+	if ( strcasecmp( s.c_str(), "may") == 0 )
+		return 4;
+	if ( strcasecmp( s.c_str(), "jun") == 0 )
+		return 5;
+	if ( strcasecmp( s.c_str(), "jul") == 0 )
+		return 6;
+	if ( strcasecmp( s.c_str(), "aug") == 0 )
+		return 7;
+	if ( strcasecmp( s.c_str(), "sep") == 0 )
+		return 8;
+	if ( strcasecmp( s.c_str(), "oct") == 0 )
+		return 9;
+	if ( strcasecmp( s.c_str(), "nov") == 0 )
+		return 10;
+	if ( strcasecmp( s.c_str(), "dec") == 0 )
+		return 11;
+	else
+		return -1;
+}
+}
+
+
+time_t
+SSubjectId::
+str_to_dob( const string& s)
+{
+	struct tm t;
+	memset( &t, '\0', sizeof (t));
+
+	// strptime( s, "%d-", &t); // will suck in non-US locales, so
+	auto ff = agh::str::tokens(s, "-");
+	if ( ff.size() != 3 )
+		return (time_t)0;
+	auto f = ff.begin();
+	try {
+		t.tm_mday = stoi( *f++);
+		t.tm_mon  = str_to_english_month(*f++);
+		t.tm_year = 1900 + stoi(*f);
+		return mktime( &t);
+	} catch (...) {
+		return (time_t)0;
+	}
+}
+
+
+// Local Variables:
+// Mode: c++
+// indent-tabs-mode: 8
+// End:
diff --git a/src/common/subject_id.hh b/src/common/subject_id.hh
new file mode 100644
index 0000000..4827df5
--- /dev/null
+++ b/src/common/subject_id.hh
@@ -0,0 +1,72 @@
+/*
+ *       File name:  common/subject_id.hh
+ *         Project:  Aghermann
+ *          Author:  Andrei Zavada <johnhommer at gmail.com>
+ * Initial version:  2013-04-13
+ *
+ *         Purpose:  subject_id shared between agh::CSubject and libsigfile::CSource
+ *
+ *         License:  GPL
+ */
+
+#ifndef _AGH_SUBJECT_ID
+#define _AGH_SUBJECT_ID
+
+#include <ctime>
+
+#include <string>
+#include <cstring>
+
+#if HAVE_CONFIG_H && !defined(VERSION)
+#  include "config.h"
+#endif
+
+using namespace std;
+
+namespace agh {
+
+
+// follow http://www.edfplus.info/specs/edfplus.html#datarecords, section 2.1.3.3
+struct SSubjectId {
+	string	id,
+		name;
+	time_t	dob;
+	enum class TGender : char {
+		unknown = 'X', male = 'M', female = 'F'
+	};
+	TGender	gender;
+
+	SSubjectId ( const string& id_ = "", const string& name_ = "",
+		     time_t dob_ = (time_t)0,
+		     TGender gender_ = TGender::unknown)
+	      : id (id_),
+		name (name_),
+		dob (dob_),
+		gender (gender_)
+		{}
+	SSubjectId (SSubjectId&& rv)
+		{
+			id.swap( rv.id);
+			name.swap( rv.name);
+			dob = rv.dob;
+			gender = rv.gender;
+		}
+
+	char gender_sign() const
+		{
+			return gender_sign(gender);
+		}
+
+	static char gender_sign( TGender);
+	static TGender char_to_gender( char);
+	static time_t str_to_dob( const string&);
+};
+
+} // namespace agh
+
+#endif
+
+// Local Variables:
+// Mode: c++
+// indent-tabs-mode: 8
+// End:
diff --git a/src/expdesign/primaries.cc b/src/expdesign/primaries.cc
index 86efc10..ba19774 100644
--- a/src/expdesign/primaries.cc
+++ b/src/expdesign/primaries.cc
@@ -27,7 +27,7 @@
 using namespace std;
 using namespace agh;
 
-
+using confval::SValidator;
 
 agh::CExpDesign::
 CExpDesign (const string& session_dir_,
@@ -40,46 +40,46 @@ CExpDesign (const string& session_dir_,
 	swa_laden_pages_before_SWA_0 (3),
 	_id_pool (0),
 	config_keys_g ({
-		confval::SValidator<double>("ctl_param.step_size",	&ctl_params0.siman_params.step_size),
-		confval::SValidator<double>("ctl_param.boltzmann_k",	&ctl_params0.siman_params.k,			confval::SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
-		confval::SValidator<double>("ctl_param.t_initial",	&ctl_params0.siman_params.t_initial,		confval::SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
-		confval::SValidator<double>("ctl_param.damping_mu",	&ctl_params0.siman_params.mu_t,			confval::SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
-		confval::SValidator<double>("ctl_param.t_min",		&ctl_params0.siman_params.t_min,		confval::SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
-		confval::SValidator<double>("profile.req_scored_pc",	&req_percent_scored,				confval::SValidator<double>::SVFRangeIn( 80., 100.)),
-		confval::SValidator<double>("fft_param.binsize",	&fft_params.binsize,				confval::SValidator<double>::SVFRangeIn( .125, 1.)),
-		confval::SValidator<double>("artifacts.dampen_factor",	&af_dampen_factor,				confval::SValidator<double>::SVFRangeIn( 0., 1.)),
-		confval::SValidator<double>("mc_param.mc_gain",		&mc_params.mc_gain,				confval::SValidator<double>::SVFRangeIn( 0., 100.)),
-		confval::SValidator<double>("mc_param.f0fc",		&mc_params.f0fc,				confval::SValidator<double>::SVFRangeEx( 0., 80.)),
-		confval::SValidator<double>("mc_param.bandwidth",	&mc_params.bandwidth,				confval::SValidator<double>::SVFRangeIn( 0.125, 2.)),
-		confval::SValidator<double>("mc_param.iir_backpolate",	&mc_params.iir_backpolate,			confval::SValidator<double>::SVFRangeIn( 0., 1.)),
-		confval::SValidator<double>("swu_param.min_upswing_duration",
-					    				&swu_params.min_upswing_duration,		confval::SValidator<double>::SVFRangeIn( 0.01, 1.)),
+		SValidator<double>("ctl_param.step_size",	&ctl_params0.siman_params.step_size),
+		SValidator<double>("ctl_param.boltzmann_k",	&ctl_params0.siman_params.k,			SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
+		SValidator<double>("ctl_param.t_initial",	&ctl_params0.siman_params.t_initial,		SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
+		SValidator<double>("ctl_param.damping_mu",	&ctl_params0.siman_params.mu_t,			SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
+		SValidator<double>("ctl_param.t_min",		&ctl_params0.siman_params.t_min,		SValidator<double>::SVFRangeEx( DBL_MIN, 1e9)),
+		SValidator<double>("profile.req_scored_pc",	&req_percent_scored,				SValidator<double>::SVFRangeIn( 80., 100.)),
+		SValidator<double>("fft_param.binsize",	&fft_params.binsize,				SValidator<double>::SVFRangeIn( .125, 1.)),
+		SValidator<double>("artifacts.dampen_factor",	&af_dampen_factor,				SValidator<double>::SVFRangeIn( 0., 1.)),
+		SValidator<double>("mc_param.mc_gain",		&mc_params.mc_gain,				SValidator<double>::SVFRangeIn( 0., 100.)),
+		SValidator<double>("mc_param.f0fc",		&mc_params.f0fc,				SValidator<double>::SVFRangeEx( 0., 80.)),
+		SValidator<double>("mc_param.bandwidth",	&mc_params.bandwidth,				SValidator<double>::SVFRangeIn( 0.125, 2.)),
+		SValidator<double>("mc_param.iir_backpolate",	&mc_params.iir_backpolate,			SValidator<double>::SVFRangeIn( 0., 1.)),
+		SValidator<double>("swu_param.min_upswing_duration",
+					    				&swu_params.min_upswing_duration,		SValidator<double>::SVFRangeIn( 0.01, 1.)),
 	}),
 	config_keys_d ({
-		confval::SValidator<int>("fft_param.welch_window_type",	(int*)&fft_params.welch_window_type,		confval::SValidator<int>::SVFRangeIn( 0, (int)sigproc::TWinType_total - 1)),
-		confval::SValidator<int>("fft_param.plan_type",		(int*)&fft_params.plan_type,			confval::SValidator<int>::SVFRangeIn( 0, (int)metrics::psd::TFFTWPlanType_total - 1)),
-		confval::SValidator<int>("artifacts.dampen_window_type",(int*)&af_dampen_window_type,			confval::SValidator<int>::SVFRangeIn( 0, (int)sigproc::TWinType_total - 1)),
-		confval::SValidator<int>("ctl_param.iters_fixed_t",	&ctl_params0.siman_params.iters_fixed_T,	confval::SValidator<int>::SVFRangeIn( 1, 1000000)),
-		confval::SValidator<int>("ctl_param.n_tries",		&ctl_params0.siman_params.n_tries,		confval::SValidator<int>::SVFRangeIn( 1, 10000)),
+		SValidator<int>("fft_param.welch_window_type",	(int*)&fft_params.welch_window_type,		SValidator<int>::SVFRangeIn( 0, (int)sigproc::TWinType_total - 1)),
+		SValidator<int>("fft_param.plan_type",		(int*)&fft_params.plan_type,			SValidator<int>::SVFRangeIn( 0, (int)metrics::psd::TFFTWPlanType_total - 1)),
+		SValidator<int>("artifacts.dampen_window_type",(int*)&af_dampen_window_type,			SValidator<int>::SVFRangeIn( 0, (int)sigproc::TWinType_total - 1)),
+		SValidator<int>("ctl_param.iters_fixed_t",	&ctl_params0.siman_params.iters_fixed_T,	SValidator<int>::SVFRangeIn( 1, 1000000)),
+		SValidator<int>("ctl_param.n_tries",		&ctl_params0.siman_params.n_tries,		SValidator<int>::SVFRangeIn( 1, 10000)),
 	}),
 	config_keys_z ({
-		confval::SValidator<size_t>("smp.num_threads",		&num_threads,					confval::SValidator<size_t>::SVFRangeIn( 0, 20)),
-		confval::SValidator<size_t>("mc_params.n_bins",		&mc_params.n_bins,				confval::SValidator<size_t>::SVFRangeIn( 1, 100)),
-		confval::SValidator<size_t>("profile.swa_laden_pages_before_SWA_0",
-					 				&swa_laden_pages_before_SWA_0,			confval::SValidator<size_t>::SVFRangeIn( 1, 100)),
-		confval::SValidator<size_t>("fft_param.pagesize",	&fft_params.pagesize,				confval::SValidator<size_t>::SVFRangeIn( 4, 120)),
-		confval::SValidator<size_t>("mc_param.smooth_side",	&mc_params.smooth_side,				confval::SValidator<size_t>::SVFRangeIn( 0, 5)),
+		SValidator<size_t>("smp.num_threads",		&num_threads,					SValidator<size_t>::SVFRangeIn( 0, 20)),
+		SValidator<size_t>("mc_params.n_bins",		&mc_params.n_bins,				SValidator<size_t>::SVFRangeIn( 1, 100)),
+		SValidator<size_t>("profile.swa_laden_pages_before_SWA_0",
+					 				&swa_laden_pages_before_SWA_0,			SValidator<size_t>::SVFRangeIn( 1, 100)),
+		SValidator<size_t>("fft_param.pagesize",	&fft_params.pagesize,				SValidator<size_t>::SVFRangeIn( 4, 120)),
+		SValidator<size_t>("mc_param.smooth_side",	&mc_params.smooth_side,				SValidator<size_t>::SVFRangeIn( 0, 5)),
 	}),
 	config_keys_b ({
-		confval::SValidator<bool>("ctl_param.DBAmendment1",	&ctl_params0.DBAmendment1),
-		confval::SValidator<bool>("ctl_param.DBAmendment2",	&ctl_params0.DBAmendment2),
-		confval::SValidator<bool>("ctl_param.AZAmendment1",	&ctl_params0.AZAmendment1),
-		confval::SValidator<bool>("ctl_param.AZAmendment2",	&ctl_params0.AZAmendment2),
-		confval::SValidator<bool>("profile.score_unscored_as_wake",
+		SValidator<bool>("ctl_param.DBAmendment1",	&ctl_params0.DBAmendment1),
+		SValidator<bool>("ctl_param.DBAmendment2",	&ctl_params0.DBAmendment2),
+		SValidator<bool>("ctl_param.AZAmendment1",	&ctl_params0.AZAmendment1),
+		SValidator<bool>("ctl_param.AZAmendment2",	&ctl_params0.AZAmendment2),
+		SValidator<bool>("profile.score_unscored_as_wake",
 					  				&score_unscored_as_wake),
 	}),
 	config_keys_s ({
-		confval::SValidator<string>("LastUsedVersion",			&last_used_version),
+		SValidator<string>("LastUsedVersion",			&last_used_version),
 	})
 {
 	char *tmp = canonicalize_file_name(session_dir_.c_str());
@@ -107,7 +107,7 @@ CExpDesign (const string& session_dir_,
 	mc_params.scope = fft_params.pagesize;
 
 #ifdef _OPENMP
-	omp_set_num_threads( (num_threads == 0) ? agh::global::num_procs : num_threads);
+	omp_set_num_threads( (num_threads == 0) ? global::num_procs : num_threads);
 	printf( "SMP enabled with %d threads\n", omp_get_max_threads());
 #endif
 	if ( last_used_version != VERSION ) {
@@ -389,57 +389,6 @@ used_samplerates( sigfile::SChannel::TType type) const
 
 
 
-const char*
-__attribute__ ((const))
-agh::CSubject::
-gender_sign( TGender g)
-{
-	switch ( g ) {
-	case TGender::male:
-		return "M";
-	case TGender::female:
-		return "F";
-	case TGender::neuter:
-		return "o";
-	default:
-		return "??";
-	}
-}
-
-agh::CSubject::
-CSubject (const string& dir,
-	  sid_type id)
-  : short_name (dir.substr( dir.rfind('/')+1)),
-    gender (TGender::neuter),
-    age (21),
-    _status (0),
-    _id (id),
-    _dir (dir)
-{
-	ifstream ifs (_dir + "/.subject_info");
-	char gender_char;
-	if ( ifs.good() and
-	     (getline( ifs, full_name, '\n'),
-	      ifs >> gender_char >> age,
-	      getline( ifs, comment, '\n'),
-	      ifs.good()) )
-		gender = (TGender)gender_char;
-	else
-		full_name = short_name;
-}
-
-
-agh::CSubject::
-~CSubject ()
-{
-	ofstream ofs (_dir + "/.subject_info");
-	char gender_char = (char)gender;
-	if ( ofs.good() )
-		ofs << full_name << endl
-		    << gender_char << endl
-		    << age << endl
-		    << comment << endl;
-}
 
 
 
diff --git a/src/expdesign/primaries.hh b/src/expdesign/primaries.hh
index 00fab5c..d29387f 100644
--- a/src/expdesign/primaries.hh
+++ b/src/expdesign/primaries.hh
@@ -23,6 +23,7 @@
 #include <stdexcept>
 
 #include "common/config-validate.hh"
+#include "common/subject_id.hh"
 #include "sigproc/winfun.hh"
 #include "model/achermann.hh"
 #include "recording.hh"
@@ -41,27 +42,25 @@ using namespace std;
 typedef size_t sid_type;
 
 
-class CSubject {
+class CSubject : public SSubjectId {
 
 	void operator=( const CSubject&) = delete;
 	CSubject () = delete;
 
     public:
-	enum class TGender : char {
-		neuter = 'o', male = 'M', female = 'F'
-	};
-	static const char* gender_sign( TGender g);
-
-    public:
-	string	short_name,
-		full_name;
-	TGender	gender;
-	int	age;
+	float	age() const;
 	string	comment;
 
 	const string&     dir() const   { return _dir; }
 
-	CSubject (const string& dir, sid_type id);
+	CSubject (const CSubject&) = default;
+	CSubject (const string& dir, sid_type id)
+	      : agh::SSubjectId (dir.substr( dir.rfind('/')+1)),
+		_status (0),
+		_id (id),
+		_dir (dir)
+		{}
+
        ~CSubject ();
 
 	class SEpisodeSequence;
@@ -221,11 +220,11 @@ class CSubject {
 
 	bool operator==( const CSubject &o) const
 		{
-			return short_name == o.short_name;
+			return id == o.id;
 		}
 	bool operator==( const string& n) const
 		{
-			return short_name == n;
+			return SSubjectId::id == n;
 		}
 	bool operator==( sid_type id) const
 		{
@@ -328,7 +327,7 @@ class CExpDesign {
 		{
 			map<string, CJGroup>::const_iterator G;
 			const CSubject& J = subject_by_x(j, &G);
-			return _session_dir + '/' + G->first + '/' + J.short_name;
+			return _session_dir + '/' + G->first + '/' + J.SSubjectId::id;
 		}
 
       // scan tree: build all structures
diff --git a/src/expdesign/recording.hh b/src/expdesign/recording.hh
index e197099..71b66fd 100644
--- a/src/expdesign/recording.hh
+++ b/src/expdesign/recording.hh
@@ -153,7 +153,7 @@ class CRecording {
 		    const metrics::mc::SPPack&);
        ~CRecording ();
 
-	const char* subject() const      {  return _source().name.c_str(); }
+	const char* subject() const      {  return _source().subject().name.c_str(); }
 	const char* session() const      {  return _source().session(); }
 	const char* episode() const      {  return _source().episode(); }
 	const char* channel() const      {  return _source().channel_by_id(_sig_no); }
diff --git a/src/expdesign/tree-scanner.cc b/src/expdesign/tree-scanner.cc
index e074779..d2333c8 100644
--- a/src/expdesign/tree-scanner.cc
+++ b/src/expdesign/tree-scanner.cc
@@ -100,7 +100,6 @@ add_one( sigfile::CTypedSource&& Fmc,
 	// printf( "E0 %s: ", e0.name());
 	// puts( asctime( localtime(&e0.start_time())));
 	// puts( asctime( localtime(&e0.start_rel)));
-	// printf( "--\n");
 	double shift = difftime( e0.start_rel, e0.start_time());
 	e0.end_rel   = e0.end_time() + shift;
 
@@ -151,16 +150,16 @@ register_intree_source( sigfile::CTypedSource&& F,
 		}
 
 		// refuse to register sources of wrong subjects
-		if ( j_name != F().id ) {
+		if ( j_name != F()._subject.id ) {
 			log_message( "%s: file belongs to subject %s (\"%s\"), is misplaced here under subject \"%s\"\n",
-				     F().filename(), F().id.c_str(), F().name.c_str(), j_name.c_str());
+				     F().filename(), F()._subject.id.c_str(), F()._subject.name.c_str(), j_name.c_str());
 			return -1;
 		}
 		try {
-			auto existing_group = group_of( F().id.c_str());
+			auto existing_group = group_of( F()._subject.id.c_str());
 			if ( g_name != existing_group ) {
 				log_message( "%s: subject %s (\"%s\") belongs to a different group (\"%s\")\n",
-					     F().filename(), F().id.c_str(), F().name.c_str(), existing_group);
+					     F().filename(), F()._subject.id.c_str(), F()._subject.name.c_str(), existing_group);
 				return -1;
 			}
 		} catch (invalid_argument) {
@@ -190,7 +189,7 @@ register_intree_source( sigfile::CTypedSource&& F,
 
 	      // insert/update episode observing start/end times
 		printf( "\nCExpDesign::register_intree_source( file: \"%s\", J: %s (\"%s\"), E: \"%s\", D: \"%s\")\n",
-			F().filename(), F().id.c_str(), F().name.c_str(), F().episode(), F().session());
+			F().filename(), F().subject().id.c_str(), F().subject().name.c_str(), F().episode(), F().session());
 		switch ( J->measurements[F().session()].add_one(
 				 move(F), fft_params, swu_params, mc_params) ) {  // this will do it
 		case AGH_EPSEQADD_OVERLAP:
@@ -319,7 +318,7 @@ scan_tree( TMsmtCollectProgressIndicatorFun user_progress_fun)
 				if ( D.second.episodes.size() < n_episodes &&
 				     complete_episode_set.front() != D.second.episodes.begin()->name() ) { // the baseline is missing
 					log_message( "No Baseline episode in %s's %s: skip this session\n",
-						     J.short_name.c_str(), D.first.c_str());
+						     J.id.c_str(), D.first.c_str());
 					J.measurements.erase(D.first);
 					goto startover;
 				}
diff --git a/src/libsigfile/edf.cc b/src/libsigfile/edf.cc
index 123cab6..5004b6b 100644
--- a/src/libsigfile/edf.cc
+++ b/src/libsigfile/edf.cc
@@ -73,9 +73,8 @@ set_session( const char* s)
 
 int
 sigfile::CEDFFile::
-set_comment( const char *s)
+set_reserved( const char *s)
 {
-	fprintf( stderr, "Writing to reserved EDF field: don't do that!\n");
 	memcpy( header.reserved, agh::str::pad( s, 44).c_str(), 44);
 	return strlen(s) > 44;
 }
@@ -155,6 +154,7 @@ CEDFFile (const char *fname_, int flags_)
       // artifacts, per signal
 	if ( flags_ & sigfile::CTypedSource::no_ancillary_files )
 		return;
+
       // else read artifacts, filters and annotations from external files
 	for ( auto &H : channels ) {
 		ifstream thomas (make_fname_artifacts( H.label));
@@ -265,7 +265,7 @@ CEDFFile (const char *fname_, TSubtype subtype_, int flags_,
 	_lay_out_header();
 
 	strncpy( header.version_number, version_string, 8);
-	set_patient_id( "Fafa_1 M X Mr._Fafa");
+	_subject.id = "Fafa_1";
 	set_recording_id( "Zzz");
 	set_comment( fname_);
 	set_start_time( time(NULL));
@@ -547,16 +547,18 @@ _parse_header()
 	      // sub-parse patient_id into SSubjectId struct
 		{
 			auto subfields = agh::str::tokens( _patient_id, " ");
-			if ( subfields.size() != 4 ) {
+			if ( unlikely (_patient_id.empty()) ) {
+				fprintf( stderr, "%s: Missing patient_id\n", filename());
+				_subject.id = _subject.name = "Fafa";
+			} else if ( subfields.size() != 4 ) {
 				fprintf( stderr, "%s: Nonconforming patient_id\n", filename());
-				SSubjectId::id = SSubjectId::name = subfields.front();
-				SSubjectId::gender = TGender::unknown;
+				_subject.id = _subject.name = subfields.front();
 			} else {
 				auto i = subfields.begin();
-				SSubjectId::id = *i++;
-				SSubjectId::gender = SSubjectId::char_to_gender((*i++)[0]);
-				SSubjectId::dob = SSubjectId::str_to_dob(*i++);
-				SSubjectId::name = agh::str::join( agh::str::tokens(*i++, "_"), " ");
+				_subject.id = *i++;
+				_subject.gender = agh::SSubjectId::char_to_gender((*i++)[0]);
+				_subject.dob = agh::SSubjectId::str_to_dob(*i++);
+				_subject.name = agh::str::join( agh::str::tokens(*i++, "_"), " ");
 			}
 		}
 
diff --git a/src/libsigfile/edf.hh b/src/libsigfile/edf.hh
index cd482e8..f20fc5d 100644
--- a/src/libsigfile/edf.hh
+++ b/src/libsigfile/edf.hh
@@ -118,13 +118,15 @@ class CEDFFile
 		{ return n_data_records * data_record_size; }
 
 	// setters
-	int set_patient_id( const char* s);
-	int set_recording_id( const char* s);
-	int set_episode( const char* s);
-	int set_session( const char* s);
-	int set_comment( const char *s);
-	int set_start_time( time_t s);
-
+	int set_patient_id( const char*);
+	int set_recording_id( const char*);
+	int set_episode( const char*);
+	int set_session( const char*);
+	int set_reserved( const char*);
+	int set_comment( const char* s)
+		{ return set_reserved( s); }
+		
+	int set_start_time( time_t);
 	// channels
 	size_t n_channels() const
 		{ return channels.size(); }
diff --git a/src/libsigfile/source-base.cc b/src/libsigfile/source-base.cc
index 0cf16ab..6c25a72 100644
--- a/src/libsigfile/source-base.cc
+++ b/src/libsigfile/source-base.cc
@@ -109,8 +109,8 @@ sigfile::SFilterPack::
 dirty_signature() const
 {
 	DEF_UNIQUE_CHARP (tmp);
-	assert (asprintf( &tmp, "%g%d%g%d%d",
-			  low_pass_cutoff, low_pass_order, high_pass_cutoff, high_pass_order, (int)notch_filter));
+	ASPRINTF( &tmp, "%g%d%g%d%d",
+		  low_pass_cutoff, low_pass_order, high_pass_cutoff, high_pass_order, (int)notch_filter);
 	return hash<std::string>() (tmp);
 }
 
@@ -121,7 +121,8 @@ dirty_signature() const
 
 
 sigfile::CSource::
-CSource( CSource&& rv)
+CSource (CSource&& rv)
+      : _subject (move(rv._subject))
 {
 	swap( _filename, rv._filename);
 	_status = rv._status;
diff --git a/src/libsigfile/source-base.hh b/src/libsigfile/source-base.hh
index 45bed88..80ffeb1 100644
--- a/src/libsigfile/source-base.hh
+++ b/src/libsigfile/source-base.hh
@@ -14,7 +14,9 @@
 
 #include "common/fs.hh"
 #include "common/alg.hh"
+#include "common/subject_id.hh"
 #include "sigproc/winfun.hh"
+#include "expdesign/forward-decls.hh"
 #include "channel.hh"
 
 #if HAVE_CONFIG_H && !defined(VERSION)
@@ -186,94 +188,24 @@ struct SFilterPack {
 
 
 
-// follow http://www.edfplus.info/specs/edfplus.html#datarecords, section 2.1.3.3
-struct SSubjectId {
-	string	id,
-		name;
-	time_t	dob;
-	enum class TGender : char {
-		unknown = 'X', male = 'M', female = 'F'
-	};
-	TGender	gender;
-	static TGender char_to_gender( char x)
-		{
-			switch ( x ) {
-			case 'M':
-			case 'm':
-				return TGender::male;
-			case 'F':
-			case 'f':
-				return TGender::female;
-			default:
-				return TGender::unknown;
-			}
-		}
-	static int str_to_english_month( const string& s)
-		{
-			if ( strcasecmp( s.c_str(), "jan") == 0 )
-				return 0;
-			if ( strcasecmp( s.c_str(), "feb") == 0 )
-				return 1;
-			if ( strcasecmp( s.c_str(), "mar") == 0 )
-				return 2;
-			if ( strcasecmp( s.c_str(), "apr") == 0 )
-				return 3;
-			if ( strcasecmp( s.c_str(), "may") == 0 )
-				return 4;
-			if ( strcasecmp( s.c_str(), "jun") == 0 )
-				return 5;
-			if ( strcasecmp( s.c_str(), "jul") == 0 )
-				return 6;
-			if ( strcasecmp( s.c_str(), "aug") == 0 )
-				return 7;
-			if ( strcasecmp( s.c_str(), "sep") == 0 )
-				return 8;
-			if ( strcasecmp( s.c_str(), "oct") == 0 )
-				return 9;
-			if ( strcasecmp( s.c_str(), "nov") == 0 )
-				return 10;
-			if ( strcasecmp( s.c_str(), "dec") == 0 )
-				return 11;
-			else
-				return -1;
-		}
-	static time_t str_to_dob( const string& s)
-		{
-			struct tm t;
-			memset( &t, '\0', sizeof (t));
-
-			// strptime( s, "%d-", &t); // will suck in non-US locales, so
-			auto ff = agh::str::tokens(s, "-");
-			if ( ff.size() != 3 )
-				return (time_t)0;
-			auto f = ff.begin();
-			try {
-				t.tm_mday = stoi( *f++);
-				t.tm_mon  = str_to_english_month(*f++);
-				t.tm_year = 1900 + stoi(*f);
-				return mktime( &t);
-			} catch (...) {
-				return (time_t)0;
-			}
-		}
-};
-
-
-
-class CSource : public SSubjectId {
+class CSource {
 	friend class CTypedSource;
+	friend class agh::CSubject;
+	friend class agh::CExpDesign;
     protected:
 	string	_filename;
 	int	_status;
 	int	_flags;
+	agh::SSubjectId
+		_subject;
     public:
 	DELETE_DEFAULT_METHODS (CSource);
-	CSource (const string& fname, int flags = 0)
-	      : _filename (fname),
+	CSource (const string& fname_, int flags_ = 0)
+	      : _filename (fname_),
 		_status (0),
-		_flags (flags)
+		_flags (flags_)
 		{}
-	CSource( CSource&& rv);
+	CSource( CSource&&);
 	virtual ~CSource()
 		{}
 
@@ -288,6 +220,10 @@ class CSource : public SSubjectId {
 		{
 			return _filename.c_str();
 		}
+	const agh::SSubjectId& subject() const
+		{
+			return _subject;
+		}
 	virtual const char* patient_id()		const = 0;
 	virtual const char* recording_id()		const = 0;
 	virtual const char* comment()			const = 0;
diff --git a/src/metrics/mc.cc b/src/metrics/mc.cc
index 26c5e5f..9d210c1 100644
--- a/src/metrics/mc.cc
+++ b/src/metrics/mc.cc
@@ -75,7 +75,7 @@ fname_base() const
 	DEF_UNIQUE_CHARP (_);
 	ASPRINTF( &_,
 		  "%s.%s-%lu"
-		  ":%lu-%g_%g" "_%g" "_%g_%g",
+		  ":%zu-%g_%g" "_%g" "_%g_%g",
 		  _using_F().filename(), _using_F().channel_by_id(_using_sig_no),
 		  _using_F().dirty_signature( _using_sig_no),
 		  Pp.pagesize,
@@ -95,7 +95,7 @@ mirror_fname() const
 	string basename_dot = agh::fs::make_fname_base (_using_F().filename(), "", true);
 	ASPRINTF( &_,
 		  "%s-%s-%lu"
-		  ":%lu-%g_%g" "_%g" "_%g_%g" "_%g_%g@%zu"
+		  ":%zu-%g_%g" "_%g" "_%g_%g" "_%g_%g@%zu"
 		  ".mc",
 		  basename_dot.c_str(), _using_F().channel_by_id(_using_sig_no),
 		  _using_F().dirty_signature( _using_sig_no),
@@ -162,7 +162,7 @@ export_tsv( const string& fname) const
 	fprintf( f, "## Subject: %s;  Session: %s, Episode: %s recorded %.*s;  Channel: %s\n"
 		 "## Total EEG Microcontinuity course (%zu %zu-sec pages) from %g up to %g Hz in bins of %g Hz\n"
 		 "#Page\t",
-		 _using_F().name.c_str(), _using_F().session(), _using_F().episode(),
+		 _using_F().subject().name.c_str(), _using_F().session(), _using_F().episode(),
 		 (int)strlen(asctime_)-1, asctime_,
 		 _using_F().channel_by_id(_using_sig_no),
 		 pages(), Pp.pagesize, Pp.freq_from, Pp.freq_from + Pp.bandwidth * bins(), Pp.bandwidth);
@@ -196,7 +196,7 @@ export_tsv( size_t bin,
 	fprintf( f, "## Microcontinuity profile of\n"
 		 "## Subject: %s;  Session: %s, Episode: %s recorded %.*s;  Channel: %s\n"
 		 "## Course (%zu %zu-sec pages) in range %g-%g Hz\n",
-		 _using_F().name.c_str(), _using_F().session(), _using_F().episode(),
+		 _using_F().subject().name.c_str(), _using_F().session(), _using_F().episode(),
 		 (int)strlen(asctime_)-1, asctime_,
 		 _using_F().channel_by_id(_using_sig_no),
 		 pages(), Pp.pagesize, Pp.freq_from, Pp.freq_from + (bin+1) * Pp.bandwidth);
diff --git a/src/metrics/page-metrics-base.cc b/src/metrics/page-metrics-base.cc
index 3064806..082bfb8 100644
--- a/src/metrics/page-metrics-base.cc
+++ b/src/metrics/page-metrics-base.cc
@@ -217,7 +217,7 @@ export_tsv( const string& fname) const
 	char *asctime_ = asctime( localtime( &sttm));
 	fprintf( f, "## Subject: %s;  Session: %s, Episode: %s recorded %.*s;  Channel: %s\n"
 		 "#Page\t",
-		 _using_F().name.c_str(), _using_F().session(), _using_F().episode(),
+		 _using_F().subject().name.c_str(), _using_F().session(), _using_F().episode(),
 		 (int)strlen(asctime_)-1, asctime_,
 		 _using_F().channel_by_id(_using_sig_no));
 
diff --git a/src/metrics/psd.cc b/src/metrics/psd.cc
index 6137127..dbb7faf 100644
--- a/src/metrics/psd.cc
+++ b/src/metrics/psd.cc
@@ -267,7 +267,7 @@ export_tsv( const string& fname) const
 	fprintf( f, "## Subject: %s;  Session: %s, Episode: %s recorded %.*s;  Channel: %s\n"
 		 "## Total spectral power course (%zu %zu-sec pages) up to %g Hz in bins of %g Hz\n"
 		 "#Page\t",
-		 _using_F().name.c_str(), _using_F().session(), _using_F().episode(),
+		 _using_F().subject().name.c_str(), _using_F().session(), _using_F().episode(),
 		 (int)strlen(asctime_)-1, asctime_,
 		 _using_F().channel_by_id(_using_sig_no),
 		 pages(), Pp.pagesize, _bins*Pp.binsize, Pp.binsize);
@@ -303,7 +303,7 @@ export_tsv( float from, float upto,
 	fprintf( f, "PSD profile of\n"
 		 "## Subject: %s;  Session: %s, Episode: %s recorded %.*s;  Channel: %s\n"
 		 "## Course (%zu %zu-sec pages) in range %g-%g Hz\n",
-		 _using_F().name.c_str(), _using_F().session(), _using_F().episode(),
+		 _using_F().subject().name.c_str(), _using_F().session(), _using_F().episode(),
 		 (int)strlen(asctime_)-1, asctime_,
 		 _using_F().channel_by_id(_using_sig_no),
 		 pages(), Pp.pagesize, from, upto);
diff --git a/src/metrics/swu.cc b/src/metrics/swu.cc
index 0c8cafd..efe7c26 100644
--- a/src/metrics/swu.cc
+++ b/src/metrics/swu.cc
@@ -155,7 +155,7 @@ export_tsv( const string& fname) const
 	fprintf( f, "## Subject: %s;  Session: %s, Episode: %s recorded %.*s;  Channel: %s\n"
 		 "## SWU course (%zu %zu-sec pages)\n"
 		 "#Page\tSWU\n",
-		 _using_F().name.c_str(), _using_F().session(), _using_F().episode(),
+		 _using_F().subject().name.c_str(), _using_F().session(), _using_F().episode(),
 		 (int)strlen(asctime_)-1, asctime_,
 		 _using_F().channel_by_id(_using_sig_no),
 		 pages(), Pp.pagesize);

-- 
Sleep experiment manager



More information about the debian-med-commit mailing list