The following commit has been merged in the master branch:
commit 6cf9ce525ce7ac2e433c1873e3a0a9fe36878760
Author: Andrei Zavada <[email protected]>
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 <[email protected]>
  * 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 <[email protected]>
  * 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

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

Reply via email to