The following commit has been merged in the master branch:
commit 86c1ce0c12e75a6a2de7670767e65f097bcc9bd7
Author: andrei zavada <[email protected]>
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 <[email protected]>
+ * 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 <[email protected]>
+ * 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

_______________________________________________
debian-med-commit mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit

Reply via email to