[med-svn] [SCM] aghermann branch, master, updated. 99b1d5a023eee9df74b0e0d6f894516fc79435ad
Andrei Zavada
johnhommer at gmail.com
Sun Jul 7 23:04:02 UTC 2013
The following commit has been merged in the master branch:
commit 22a2a688c605d0e96da0e75a84331697c223da5b
Author: Andrei Zavada <johnhommer at gmail.com>
Date: Mon Jun 24 02:41:09 2013 +0300
WIP (sigfile::CTSVFile)
diff --git a/src/libsigfile/Makefile.am b/src/libsigfile/Makefile.am
index ac733a1..9fad844 100644
--- a/src/libsigfile/Makefile.am
+++ b/src/libsigfile/Makefile.am
@@ -16,6 +16,8 @@ libsigfile_la_SOURCES := \
edf.cc \
edf-io.cc \
edf.hh \
+ tsv.cc \
+ tsv.hh \
page.cc \
page.hh
@@ -41,6 +43,7 @@ BUILT_SOURCES := \
source-base.hh.gch \
source.hh.gch \
edf.hh.gch \
+ tsv.hh.gch \
page.hh.gch
%.hh.gch: %.hh
$(CXXCOMPILE) -c $<
diff --git a/src/libsigfile/edf-io.cc b/src/libsigfile/edf-io.cc
index 8aa3295..4253f87 100644
--- a/src/libsigfile/edf-io.cc
+++ b/src/libsigfile/edf-io.cc
@@ -146,6 +146,7 @@ get_region_filtered_smpl( const int h,
58, 62, 1, true);
break;
case SFilterPack::TNotchFilter::none:
+ default:
break;
}
diff --git a/src/libsigfile/edf.cc b/src/libsigfile/edf.cc
index 5a54285..5aee4ee 100644
--- a/src/libsigfile/edf.cc
+++ b/src/libsigfile/edf.cc
@@ -35,11 +35,14 @@ using agh::str::tokens_trimmed;
using sigfile::CEDFFile;
+
+// every setter is special
int
CEDFFile::
set_patient_id( const string& s)
{
memcpy( header.patient_id, pad( s, 80).c_str(), 80);
+ _patient_id = s;
return s.size() > 80;
}
@@ -48,6 +51,8 @@ CEDFFile::
set_recording_id( const string& s)
{
memcpy( header.recording_id, pad( s, 80).c_str(), 80);
+ _recording_id = s;
+ // maybe let _session and _episode be assigned, too?
return s.size() > 80;
}
@@ -56,6 +61,7 @@ CEDFFile::
set_episode( const string& s)
{
_episode.assign( s);
+ // aha
return set_recording_id( (_session + '/' + _episode).c_str());
}
@@ -69,8 +75,10 @@ set_session( const string& s)
int
CEDFFile::
-set_reserved( const string&s)
+set_reserved( const string& s)
{
+ fprintf( stderr, "You just voided your warranty: Writing to \"reserved\" field in EDF header:\n%s\n", s.c_str());
+ _recording_id = s;
memcpy( header.reserved, pad( s, 44).c_str(), 44);
return s.size() > 44;
}
@@ -115,17 +123,13 @@ CEDFFile (const string& fname_, const int flags_)
{
struct stat stat0;
int stst = stat( fname_.c_str(), &stat0);
- if ( stst == -1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument (explain_edf_status(_status));
- }
+ if ( stst == -1 )
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
_fsize = stat0.st_size;
}
_fd = open( fname_.c_str(), O_RDWR);
- if ( _fd == -1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument (explain_edf_status(_status));
- }
+ if ( _fd == -1 )
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
// mmap
_mmapping =
@@ -143,7 +147,7 @@ CEDFFile (const string& fname_, const int flags_)
if ( not (flags_ & no_field_consistency_check) ) {
close( _fd);
munmap( _mmapping, _fsize);
- throw invalid_argument (explain_edf_status(_status));
+ throw invalid_argument (explain_status(_status));
} else
fprintf( stderr, "CEDFFile::CEDFFile(\"%s\") Warning: parse header failed, but proceeding anyway\n", fname_.c_str());
}
@@ -162,7 +166,7 @@ CEDFFile (const string& fname_, const int flags_)
close( _fd);
munmap( _mmapping, _fsize);
_status |= file_truncated;
- throw invalid_argument (explain_edf_status(_status));
+ throw invalid_argument (explain_status(_status));
} else if ( _fsize > expected_fsize ) {
_status |= trailing_junk;
fprintf( stderr, "CEDFFile::CEDFFile(\"%s\") Warning: %zu bytes of trailing junk\n",
@@ -173,71 +177,10 @@ CEDFFile (const string& fname_, const int flags_)
_extract_embedded_annotations();
// ancillary files:
- if ( flags_ & sigfile::CTypedSource::no_ancillary_files )
+ if ( flags_ & sigfile::CSource::no_ancillary_files )
;
- else {
- // 1. artifacts, per signal
- for ( auto &H : channels ) {
- ifstream thomas (make_fname_artifacts( H.ucd));
- if ( not thomas.good() )
- continue;
-
- while ( !thomas.eof() ) {
- double aa = NAN, az = NAN;
- thomas >> aa >> az;
- if ( not isfinite(aa) || not isfinite(az) )
- break;
- H.artifacts.mark_artifact( aa, az);
- }
- }
-
- // 2. annotations, per signal
- for ( auto &H : channels ) {
- ifstream fd (make_fname_annotations( H.ucd));
- if ( not fd.good() )
- continue;
- while ( fd.good() and not fd.eof() ) {
- int type = -1;
- double aa = NAN, az = NAN;
- string an;
- fd >> type >> aa >> az;
- getline( fd, an, EOA);
- if ( isfinite(aa) and isfinite(az) and
- aa < az and az < n_data_records * data_record_size
- and type < SAnnotation::TType_total and type >= 0 )
- H.annotations.emplace_back(
- aa, az,
- trim(an),
- (SAnnotation::TType)type);
- else {
- fprintf( stderr, "Bad annotation: (%d %g %g %50s)\n", type, aa, az, an.c_str());
- break;
- }
- }
- H.annotations.sort();
- }
-
- // 3. filters
- {
- ifstream thomas (make_fname_filters(fname_));
- if ( !thomas.fail() )
- for ( auto &I : channels ) {
- int ol = -1, oh = -1, nf = -1;
- float fl = 0., fh = 0.;
- thomas >> fl >> ol
- >> fh >> oh >> nf;
- if ( ol > 0 && oh > 0 && ol < 5 && oh < 5
- && fl >= 0. && fh >= 0.
- && nf >= 0 && nf <= 2 ) {
- I.filters.low_pass_cutoff = fl;
- I.filters.low_pass_order = ol;
- I.filters.high_pass_cutoff = fh;
- I.filters.high_pass_order = oh;
- I.filters.notch_filter = (SFilterPack::TNotchFilter)nf;
- }
- }
- }
- }
+ else
+ load_ancillary_files();
}
@@ -254,10 +197,8 @@ CEDFFile (const string& fname_, const TSubtype subtype_, const int flags_,
_subtype (subtype_)
{
_fd = open( fname_.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP);
- if ( _fd == -1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument ("CEDFFile::CEDFFile(): file open error");
- }
+ if ( _fd == -1 )
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
header_length = 256 + (channels_.size() * 256);
size_t total_samplerate = 0;
@@ -266,10 +207,8 @@ CEDFFile (const string& fname_, const TSubtype subtype_, const int flags_,
_fsize = header_length + 2 * total_samplerate * data_record_size * n_data_records;
// extend
- if ( lseek( _fd, _fsize-1, SEEK_SET) == -1 || write( _fd, "\0", 1) != 1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument ("CEDFFile::CEDFFile(): file write error");
- }
+ if ( lseek( _fd, _fsize-1, SEEK_SET) == -1 || write( _fd, "\0", 1) != 1 )
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
// size_t sys_page_size = (size_t) sysconf( _SC_PAGESIZE);
_mmapping =
@@ -280,7 +219,7 @@ CEDFFile (const string& fname_, const TSubtype subtype_, const int flags_,
0);
if ( _mmapping == (void*)-1 ) {
close( _fd);
- throw length_error ("CEDFFile::CEDFFile(): mmap error");
+ throw invalid_argument (explain_status(_status |= TStatus::mmap_error));
}
// fill out some essential header fields
@@ -354,7 +293,7 @@ set_digital_range( const int16_t m, const int16_t M)
/*
size_t
CEDFFile::
-resize( const size_t new_records)
+resize_records( const size_t new_records)
{
size_t total_samples_per_record = 0;
for ( auto& H : channels )
@@ -405,7 +344,10 @@ CEDFFile (CEDFFile&& rv)
_start_time = rv._start_time;
_end_time = rv._end_time;
- swap( _patient_id, rv._patient_id);
+ swap( _patient_id, rv._patient_id);
+ swap( _recording_id, rv._recording_id);
+ swap( _reserved, rv._reserved);
+
swap( _episode, rv._episode);
swap( _session, rv._session);
@@ -430,9 +372,6 @@ CEDFFile::
if ( _mmapping != (void*)-1 ) {
munmap( _mmapping, _fsize);
close( _fd);
-
- if ( not (flags() & sigfile::CTypedSource::no_ancillary_files) )
- write_ancillary_files();
}
}
@@ -441,38 +380,6 @@ CEDFFile::
-void
-CEDFFile::
-write_ancillary_files()
-{
- for ( auto &I : channels ) {
- if ( not I.artifacts().empty() ) {
- ofstream thomas (make_fname_artifacts( I.ucd), ios_base::trunc);
- if ( thomas.good() )
- for ( auto &A : I.artifacts() )
- thomas << A.a << ' ' << A.z << endl;
- } else
- if ( unlink( make_fname_artifacts( I.ucd).c_str()) ) {}
-
- if ( not I.annotations.empty() ) {
- ofstream thomas (make_fname_annotations( I.ucd), ios_base::trunc);
- for ( auto &A : I.annotations )
- thomas << (int)A.type << ' ' << A.span.a << ' ' << A.span.z << ' ' << A.label << EOA << endl;
- } else
- if ( unlink( make_fname_annotations( I.ucd).c_str()) ) {}
- }
- ofstream thomas (make_fname_filters( filename()), ios_base::trunc);
- if ( thomas.good() )
- for ( auto &I : channels )
- thomas << I.filters.low_pass_cutoff << ' ' << I.filters.low_pass_order << ' '
- << I.filters.high_pass_cutoff << ' ' << I.filters.high_pass_order << ' '
- << (int)I.filters.notch_filter << endl;
-}
-
-
-
-
-
@@ -531,8 +438,6 @@ _get_next_field( char *&field, const size_t fld_size) throw (TStatus)
return field;
}
-size_t CEDFFile::max_channels = 256;
-
int
CEDFFile::
_parse_header()
@@ -573,7 +478,7 @@ _parse_header()
if ( !header_length || !n_data_records || !data_record_size || !n_channels ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
if ( n_channels == 0 ) {
@@ -582,6 +487,7 @@ _parse_header()
}
_patient_id = trim( string (header.patient_id, 80));
+ _recording_id = trim( string (header.patient_id, 80));
// sub-parse patient_id into SSubjectId struct
{
@@ -641,6 +547,7 @@ _parse_header()
}
}
+ // parse times
{
struct tm ts;
char *p;
@@ -650,14 +557,14 @@ _parse_header()
p = strptime( tmp.c_str(), "%d.%m.%y", &ts);
if ( p == NULL || *p != '\0' ) {
_status |= date_unparsable;
- if ( not (flags() & no_field_consistency_check) )
+ 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() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
@@ -670,9 +577,12 @@ _parse_header()
_end_time = _start_time + n_data_records * data_record_size;
}
+ // assign "reserved"
+ _reserved = trim( string (header.reserved, 44));
+
if ( n_channels > max_channels ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
} else {
channels.resize( n_channels);
@@ -718,7 +628,7 @@ _parse_header()
if ( sscanf( H.header.physical_min, "%8lg",
&H.physical_min) != 1 ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
}
@@ -729,7 +639,7 @@ _parse_header()
if ( sscanf( H.header.physical_max, "%8lg",
&H.physical_max) != 1 ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
}
@@ -741,7 +651,7 @@ _parse_header()
if ( sscanf( H.header.digital_min, "%8d",
&H.digital_min) != 1 ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
}
@@ -752,7 +662,7 @@ _parse_header()
if ( sscanf( H.header.digital_max, "%8d",
&H.digital_max) != 1 ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
}
@@ -768,7 +678,7 @@ _parse_header()
strtoul( t.c_str(), &tail, 10);
if ( tail == NULL || *tail != '\0' ) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -2;
}
}
@@ -781,7 +691,7 @@ _parse_header()
return -1;
} catch (invalid_argument ex) {
_status |= bad_numfld;
- if ( not (flags() & no_field_consistency_check) )
+ if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
return -3;
}
@@ -995,7 +905,7 @@ details( const int which) const
string
CEDFFile::
-explain_edf_status( const int status)
+explain_status( const int status)
{
list<string> recv;
if ( status & sysfail )
@@ -1036,6 +946,9 @@ explain_edf_status( const int status)
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");
+ if ( status & mmap_error )
+ recv.emplace_back( "* mmap error");
+
return join(recv, "\n");
}
diff --git a/src/libsigfile/edf.hh b/src/libsigfile/edf.hh
index c917f29..a8ba074 100644
--- a/src/libsigfile/edf.hh
+++ b/src/libsigfile/edf.hh
@@ -9,8 +9,8 @@
* License: GPL
*/
-#ifndef _SIGFILE_EDF_H
-#define _SIGFILE_EDF_H
+#ifndef AGH_SIGFILE_EDF_H_
+#define AGH_SIGFILE_EDF_H_
#include <cinttypes>
#include <cstring>
@@ -77,7 +77,6 @@ class CEDFFile
enum TFlags {
no_mmap = 1<<3,
no_cache = 1<<4, // just considering
- no_field_consistency_check = 1<<5,
};
// open existing
CEDFFile (const string& fname, int flags = 0);
@@ -93,21 +92,19 @@ class CEDFFile
// interface
// status
string explain_status() const
- { return explain_edf_status( _status); }
+ { return explain_status( _status); }
// identification
- const char* filename() const
- { return _filename.c_str(); }
const char* patient_id() const
{ return _patient_id.c_str(); }
const char* recording_id() const
- { return header.recording_id; }
- const char* comment() const
- { return header.reserved; }
+ { return _recording_id.c_str(); }
const char* episode() const
{ return _episode.c_str(); }
const char* session() const
{ return _session.c_str(); }
+ const char* comment() const
+ { return _reserved.c_str(); }
// times
time_t start_time() const
@@ -122,9 +119,9 @@ class CEDFFile
int set_recording_id( const string&);
int set_episode( const string&);
int set_session( const string&);
- int set_reserved( const string&);
- int set_comment( const string& s)
- { return set_reserved( s); }
+ int set_comment( const string&) // note that there's no room in EDF header to store anything useful
+ { return 1; }
+ int set_reserved( const string&); // but you can clobber "reserved" field if you must
int set_start_time( time_t);
// channels
@@ -249,18 +246,15 @@ class CEDFFile
// adjust capacity
size_t
- resize( size_t new_records);
+ resize_records( size_t new_records);
+ // unused, undefined
// export
- int
- export_original( int h, const string& fname) const;
- int
- export_filtered( int h, const string& fname) const;
+ int export_original( int h, const string& fname) const;
+ int export_filtered( int h, const string& fname) const;
- int
- export_original_( int h, const string& fname) const;
- int
- export_filtered_( int h, const string& fname) const;
+ int export_original_( int h, const string& fname) const;
+ int export_filtered_( int h, const string& fname) const;
// reporting & misc
@@ -286,7 +280,7 @@ class CEDFFile
};
SEDFHeader header;
- // (relevant converted integers)
+ // relevant converted integers
double data_record_size;
size_t n_data_records;
@@ -344,7 +338,6 @@ class CEDFFile
};
vector<SSignal>
channels;
- static size_t max_channels;
list<SAnnotation> // timepoints in seconds
common_annotations;
@@ -402,6 +395,7 @@ class CEDFFile
trailing_junk = (1 << 18),
extra_patientid_subfields = (1 << 19),
recognised_channel_conflicting_type = (1 << 20),
+ mmap_error = (1 << 21),
inoperable = (bad_header
| bad_version
@@ -412,9 +406,10 @@ class CEDFFile
| nogain
| sysfail
| too_many_channels
- | file_truncated)
+ | file_truncated
+ | mmap_error)
};
- static string explain_edf_status( int);
+ static string explain_status( int);
private:
TSubtype _subtype;
@@ -423,10 +418,12 @@ class CEDFFile
_end_time;
string _patient_id, // this is trimmed, raw; parsed into SSubjectId fields
+ _recording_id,
// take care of file being named 'episode-1.edf'
_episode,
// loosely/possibly also use RecordingID as session
- _session;
+ _session,
+ _reserved;
void _lay_out_header();
int _parse_header();
diff --git a/src/libsigfile/forward-decls.hh b/src/libsigfile/forward-decls.hh
index 4c44934..1acf89f 100644
--- a/src/libsigfile/forward-decls.hh
+++ b/src/libsigfile/forward-decls.hh
@@ -10,8 +10,8 @@
*/
-#ifndef _SIGFILE_FORWARD_DECLS_H
-#define _SIGFILE_FORWARD_DECLS_H
+#ifndef AGH_SIGFILE_FORWARD_DECLS_H_
+#define AGH_SIGFILE_FORWARD_DECLS_H_
namespace sigfile {
@@ -24,9 +24,12 @@ class CSource;
class CTypedSource;
class CHypnogram;
+class CTSVFile;
+class CEDFFile;
+
} // namespace sigfile
-#endif // _SIGFILE_FORWARD_DECLS_H
+#endif
// Local Variables:
// Mode: c++
diff --git a/src/libsigfile/source-base.cc b/src/libsigfile/source-base.cc
index 9f2405c..818894f 100644
--- a/src/libsigfile/source-base.cc
+++ b/src/libsigfile/source-base.cc
@@ -10,6 +10,8 @@
*/
+#include <fstream>
+#include "common/string.hh"
#include "source-base.hh"
using namespace std;
@@ -121,6 +123,144 @@ dirty_signature() const
+int
+sigfile::CSource::
+load_ancillary_files()
+{
+ int retval = 0;
+
+ for ( int h = 0; h < (int)n_channels(); ++h ) {
+ auto& H = channel_by_id(h);
+
+ // 1. artifacts
+ {
+ ifstream thomas (make_fname_artifacts( H));
+ if ( not thomas.good() )
+ goto step2;
+
+ auto& AA = artifacts(h);
+ while ( !thomas.eof() ) {
+ double aa = NAN, az = NAN;
+ thomas >> aa >> az;
+ if ( not isfinite(aa) || not isfinite(az) ) {
+ retval = -1;
+ break;
+ }
+ AA.mark_artifact( aa, az);
+ }
+ }
+
+ step2:
+ // 2. annotations
+ {
+ ifstream fd (make_fname_annotations( H));
+ if ( not fd.good() )
+ goto step3;
+
+ auto& AA = annotations(h);
+ while ( fd.good() and not fd.eof() ) {
+ int type = -1;
+ double aa = NAN, az = NAN;
+ string an;
+ fd >> type >> aa >> az;
+ getline( fd, an, SAnnotation::EOA);
+ if ( isfinite(aa) and isfinite(az) and
+ aa < az and az <= recording_time()
+ and type < SAnnotation::TType_total and type >= 0 )
+ AA.emplace_back(
+ aa, az,
+ agh::str::trim(an),
+ (SAnnotation::TType)type);
+ else {
+ retval = -1;
+ break;
+ }
+ }
+ AA.sort();
+ }
+ step3:
+ ;
+ }
+
+ // 3. filters
+ {
+ ifstream thomas (make_fname_filters(_filename));
+ if ( !thomas.good() )
+ for ( int h = 0; h < (int)n_channels(); ++h ) {
+ auto& AA = filters(h);
+
+ unsigned lpo = -1, hpo = -1, nf = -1;
+ double lpc = 0., hpc = 0.;
+ thomas >> lpc >> lpo
+ >> hpc >> hpo >> nf;
+ AA = {lpc, lpo, hpc, hpo, (SFilterPack::TNotchFilter)nf};
+ if ( not AA.is_valid() )
+ AA.reset();
+ }
+ }
+
+ return retval;
+}
+
+
+
+
+
+int
+sigfile::CSource::
+save_ancillary_files()
+{
+ int retval = 0;
+ for ( int h = 0; h < (int)n_channels(); ++h ) {
+ auto& H = channel_by_id(h);
+ {
+ auto& AA = artifacts(h);
+ if ( not AA.empty() ) {
+ ofstream thomas (make_fname_artifacts( H), ios_base::trunc);
+ for ( auto &A : AA() )
+ thomas << A.a << ' ' << A.z << endl;
+ if ( not thomas.good() )
+ retval = -1;
+ } else
+ if ( unlink( make_fname_artifacts( H).c_str()) ) {}
+ }
+
+ {
+ auto& AA = annotations(h);
+
+ auto fname = make_fname_annotations( H);
+
+ if ( not AA.empty() ) {
+ ofstream thomas (fname, ios_base::trunc);
+ for ( auto &A : AA ) {
+ thomas << (int)A.type << ' '
+ << A.span.a << ' ' << A.span.z << ' '
+ << A.label << SAnnotation::EOA << endl;
+ if ( not thomas.good() )
+ retval = -1;
+ }
+
+ } else
+ if ( unlink( fname.c_str()) ) {}
+ }
+ }
+ ofstream thomas (make_fname_filters( filename()), ios_base::trunc);
+ if ( thomas.good() )
+ for ( int h = 0; h < (int)n_channels(); ++h ) {
+ auto& AA = filters(h);
+ thomas << AA.low_pass_cutoff << ' ' << AA.low_pass_order << ' '
+ << AA.high_pass_cutoff << ' ' << AA.high_pass_order << ' '
+ << (int)AA.notch_filter << endl;
+ if ( not thomas.good() )
+ retval = -1;
+ }
+
+ return retval;
+}
+
+
+
+
sigfile::CSource::
diff --git a/src/libsigfile/source-base.hh b/src/libsigfile/source-base.hh
index 945d3f0..a0d77e8 100644
--- a/src/libsigfile/source-base.hh
+++ b/src/libsigfile/source-base.hh
@@ -84,6 +84,10 @@ struct SArtifacts {
return obj;
}
+ bool empty() const
+ {
+ return obj.empty();
+ }
size_t total() const
{
size_t q = 0;
@@ -109,6 +113,8 @@ struct SArtifacts {
struct SAnnotation {
+ static const char EOA = '$';
+
agh::alg::SSpan<double> span;
string label;
enum TType {
@@ -120,7 +126,7 @@ struct SAnnotation {
};
TType type;
- SAnnotation( double aa, double az, const string& l, TType t = TType::plain)
+ SAnnotation (double aa, double az, const string& l, TType t = TType::plain)
: span {aa, az},
label (l),
type (t)
@@ -154,29 +160,49 @@ mark_annotation( list<SAnnotation>& annotations,
struct SFilterPack {
- SFilterPack()
- : high_pass_cutoff (0.),
- low_pass_cutoff (0.),
- high_pass_order (0),
+ enum TNotchFilter : int {
+ none, at50Hz, at60Hz, TNotchFilter_total
+ };
+
+ SFilterPack ()
+ : low_pass_cutoff (0.),
low_pass_order (0),
+ high_pass_cutoff (0.),
+ high_pass_order (0),
notch_filter (TNotchFilter::none)
{}
+ SFilterPack (double lpo, unsigned lpc, double hpo, unsigned hpc, TNotchFilter nf)
+ : low_pass_cutoff (lpc),
+ low_pass_order (lpo),
+ high_pass_cutoff (hpc),
+ high_pass_order (hpo),
+ notch_filter (nf)
+ {}
bool have_filters() const
{
return low_pass_cutoff > 0. || high_pass_cutoff > 0. ||
notch_filter != SFilterPack::TNotchFilter::none;
}
+ bool is_valid() const
+ {
+ return high_pass_order < 5 &&
+ low_pass_order < 5 &&
+ notch_filter >= TNotchFilter::none &&
+ notch_filter < TNotchFilter::TNotchFilter_total;
+ }
+ void reset()
+ {
+ high_pass_cutoff = low_pass_cutoff = 0.;
+ high_pass_order = low_pass_order = 0;
+ notch_filter = TNotchFilter::none;
+ }
- double high_pass_cutoff,
- low_pass_cutoff;
- unsigned
- high_pass_order,
- low_pass_order;
+ double low_pass_cutoff;
+ unsigned low_pass_order;
+ double high_pass_cutoff;
+ unsigned high_pass_order;
- enum TNotchFilter : int {
- none, at50Hz, at60Hz
- };
TNotchFilter
notch_filter;
@@ -190,10 +216,20 @@ class CSource {
friend class CTypedSource;
protected:
string _filename;
+
int _status;
+ void clear_status()
+ { _status = 0; }
+
+ enum TFlags {
+ no_ancillary_files = 1<<1,
+ no_field_consistency_check = 1<<2,
+ };
int _flags;
+
agh::SSubjectId
_subject;
+
public:
DELETE_DEFAULT_METHODS (CSource);
CSource (const string& fname_, int flags_ = 0)
@@ -203,10 +239,14 @@ class CSource {
{}
CSource( CSource&&);
virtual ~CSource()
- {}
+ {
+ if ( not (_flags & no_ancillary_files) )
+ save_ancillary_files();
+ }
int status() const { return _status; }
int flags() const { return _flags; }
+
virtual string explain_status() const = 0;
virtual string details( int which_details) const = 0;
@@ -232,6 +272,8 @@ class CSource {
virtual double recording_time() const = 0;
// channels
+ const static size_t max_channels = 1024;
+
virtual size_t n_channels() const = 0;
virtual list<SChannel> channel_list() const = 0;
virtual bool have_channel( const SChannel&) const = 0;
@@ -267,13 +309,15 @@ class CSource {
virtual const SFilterPack&
filters( int) const = 0;
- template <typename T>
unsigned long
- dirty_signature( T id) const
+ dirty_signature( int id) const
{
return artifacts(id).dirty_signature() + filters(id).dirty_signature();
}
+ virtual int load_ancillary_files();
+ virtual int save_ancillary_files();
+
// setters
virtual int set_patient_id( const string&) = 0;
virtual int set_recording_id( const string&) = 0;
@@ -335,16 +379,11 @@ class CSource {
virtual int
put_region_smpl( int, const valarray<TFloat>&, size_t) const = 0;
- int
- put_region_sec( const int h, const valarray<TFloat>& src, const float offset) const
+ int put_region_sec( const int h, const valarray<TFloat>& src, const float offset) const
{ return put_region_smpl( h, src, offset * samplerate(h)); }
- int
- put_signal( const int h,
- const valarray<TFloat>& src)
- {
- return put_region_smpl( h, src, 0);
- }
+ int put_signal( const int h, const valarray<TFloat>& src)
+ { return put_region_smpl( h, src, 0); }
// signal data info
virtual pair<TFloat, TFloat>
@@ -374,9 +413,6 @@ class CSource {
{
return sigfile::make_fname_annotations( filename(), channel);
}
-
- // misc useful bits
- virtual void write_ancillary_files() = 0;
};
diff --git a/src/libsigfile/source.hh b/src/libsigfile/source.hh
index 4bd2cfa..191fb9f 100644
--- a/src/libsigfile/source.hh
+++ b/src/libsigfile/source.hh
@@ -51,7 +51,6 @@ class CTypedSource
throw invalid_argument("nono");
}
// ctor
- enum { no_ancillary_files = 1 };
CTypedSource (const string& fname, size_t pagesize, int flags = 0);
CTypedSource (CTypedSource&& rv);
~CTypedSource ();
diff --git a/src/libsigfile/edf.cc b/src/libsigfile/tsv.cc
similarity index 73%
copy from src/libsigfile/edf.cc
copy to src/libsigfile/tsv.cc
index 5a54285..2f56573 100644
--- a/src/libsigfile/edf.cc
+++ b/src/libsigfile/tsv.cc
@@ -1,20 +1,18 @@
/*
- * File name: libsigfile/edf.cc
+ * File name: libsigfile/tsv.cc
* Project: Aghermann
* Author: Andrei Zavada <johnhommer at gmail.com>
- * Initial version: 2008-07-01
+ * Initial version: 2013-06-22
*
- * Purpose: EDF class methods
+ * Purpose: TSV source
*
* License: GPL
*/
#include <sys/stat.h>
-#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
-#include <cinttypes>
#include <fstream>
#include <sstream>
#include <list>
@@ -22,7 +20,7 @@
#include "common/lang.hh"
#include "common/string.hh"
-#include "edf.hh"
+#include "tsv.hh"
#include "source.hh"
using namespace std;
@@ -33,58 +31,17 @@ using agh::str::join;
using agh::str::tokens;
using agh::str::tokens_trimmed;
-using sigfile::CEDFFile;
+using sigfile::CTSVFile;
int
-CEDFFile::
-set_patient_id( const string& s)
-{
- memcpy( header.patient_id, pad( s, 80).c_str(), 80);
- return s.size() > 80;
-}
-
-int
-CEDFFile::
-set_recording_id( const string& s)
-{
- memcpy( header.recording_id, pad( s, 80).c_str(), 80);
- return s.size() > 80;
-}
-
-int
-CEDFFile::
-set_episode( const string& s)
-{
- _episode.assign( s);
- return set_recording_id( (_session + '/' + _episode).c_str());
-}
-
-int
-CEDFFile::
-set_session( const string& s)
-{
- _session.assign( s);
- return set_recording_id( (_session + '/' + _episode).c_str());
-}
-
-int
-CEDFFile::
-set_reserved( const string&s)
-{
- memcpy( header.reserved, pad( s, 44).c_str(), 44);
- return s.size() > 44;
-}
-
-int
-CEDFFile::
+CTSVFile::
set_start_time( time_t s)
{
char b[9];
- // set start
strftime( b, 9, "%d.%m.%y", localtime(&s));
- memcpy( header.recording_date, b, 8);
+ header.recording_date.assign( b);
strftime( b, 9, "%H.%M.%s", localtime(&s));
- memcpy( header.recording_time, b, 8);
+ header.recording_time.assign( b);
return 0;
}
@@ -92,202 +49,56 @@ set_start_time( time_t s)
-
-
-
-
-
-
-
-#define EOA '$'
-
-namespace {
-
-const char version_string[8] = {'0',' ',' ',' ', ' ',' ',' ',' '};
-
-}
-
-
-CEDFFile::
-CEDFFile (const string& fname_, const int flags_)
+CTSVFile::
+CTSVFile (const string& fname_, const int flags_)
: CSource (fname_, flags_)
{
{
struct stat stat0;
int stst = stat( fname_.c_str(), &stat0);
- if ( stst == -1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument (explain_edf_status(_status));
- }
- _fsize = stat0.st_size;
+ if ( stst == -1 )
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
}
_fd = open( fname_.c_str(), O_RDWR);
- if ( _fd == -1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument (explain_edf_status(_status));
- }
-
- // mmap
- _mmapping =
- mmap( NULL,
- _fsize,
- PROT_READ | PROT_WRITE, MAP_SHARED,
- _fd, 0);
- if ( _mmapping == (void*)-1 ) {
- close( _fd);
- throw length_error ("CEDFFile::CEDFFile(): mmap error");
- }
+ if ( _fd == -1 )
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
// parse header
if ( _parse_header() ) { // creates channels list
- if ( not (flags_ & no_field_consistency_check) ) {
+ if ( not (flags_ & sigfile::CSource::no_field_consistency_check) ) {
close( _fd);
- munmap( _mmapping, _fsize);
- throw invalid_argument (explain_edf_status(_status));
+ throw invalid_argument (explain_status(_status)); // _status set in _parse_header()
} else
- fprintf( stderr, "CEDFFile::CEDFFile(\"%s\") Warning: parse header failed, but proceeding anyway\n", fname_.c_str());
+ fprintf( stderr, "CTSVFile::CTSVFile(\"%s\") Warning: parse header failed, but proceeding anyway\n", fname_.c_str());
}
// channels now available
- header_length = 256 + (channels.size() * 256);
-
- // lest we ever access past mmapped region
- {
- size_t total_samples_per_record = 0;
- for ( auto& H : channels )
- total_samples_per_record += H.samples_per_record;
- size_t expected_fsize = header_length + sizeof(int16_t) * total_samples_per_record * n_data_records;
- if ( _fsize < expected_fsize ) {
- fprintf( stderr, "CEDFFile::CEDFFile(\"%s\") file size less than declared in header\n", fname_.c_str());
- close( _fd);
- munmap( _mmapping, _fsize);
- _status |= file_truncated;
- throw invalid_argument (explain_edf_status(_status));
- } else if ( _fsize > expected_fsize ) {
- _status |= trailing_junk;
- fprintf( stderr, "CEDFFile::CEDFFile(\"%s\") Warning: %zu bytes of trailing junk\n",
- fname_.c_str(), _fsize - expected_fsize);
- }
- }
-
- _extract_embedded_annotations();
-
// ancillary files:
- if ( flags_ & sigfile::CTypedSource::no_ancillary_files )
- ;
- else {
- // 1. artifacts, per signal
- for ( auto &H : channels ) {
- ifstream thomas (make_fname_artifacts( H.ucd));
- if ( not thomas.good() )
- continue;
-
- while ( !thomas.eof() ) {
- double aa = NAN, az = NAN;
- thomas >> aa >> az;
- if ( not isfinite(aa) || not isfinite(az) )
- break;
- H.artifacts.mark_artifact( aa, az);
- }
- }
-
- // 2. annotations, per signal
- for ( auto &H : channels ) {
- ifstream fd (make_fname_annotations( H.ucd));
- if ( not fd.good() )
- continue;
- while ( fd.good() and not fd.eof() ) {
- int type = -1;
- double aa = NAN, az = NAN;
- string an;
- fd >> type >> aa >> az;
- getline( fd, an, EOA);
- if ( isfinite(aa) and isfinite(az) and
- aa < az and az < n_data_records * data_record_size
- and type < SAnnotation::TType_total and type >= 0 )
- H.annotations.emplace_back(
- aa, az,
- trim(an),
- (SAnnotation::TType)type);
- else {
- fprintf( stderr, "Bad annotation: (%d %g %g %50s)\n", type, aa, az, an.c_str());
- break;
- }
- }
- H.annotations.sort();
- }
-
- // 3. filters
- {
- ifstream thomas (make_fname_filters(fname_));
- if ( !thomas.fail() )
- for ( auto &I : channels ) {
- int ol = -1, oh = -1, nf = -1;
- float fl = 0., fh = 0.;
- thomas >> fl >> ol
- >> fh >> oh >> nf;
- if ( ol > 0 && oh > 0 && ol < 5 && oh < 5
- && fl >= 0. && fh >= 0.
- && nf >= 0 && nf <= 2 ) {
- I.filters.low_pass_cutoff = fl;
- I.filters.low_pass_order = ol;
- I.filters.high_pass_cutoff = fh;
- I.filters.high_pass_order = oh;
- I.filters.notch_filter = (SFilterPack::TNotchFilter)nf;
- }
- }
- }
- }
+ if ( not (flags_ & sigfile::CSource::no_ancillary_files) )
+ load_ancillary_files();
}
-CEDFFile::
-CEDFFile (const string& fname_, const TSubtype subtype_, const int flags_,
- const list<pair<SChannel, size_t>>& channels_,
- const size_t data_record_size_,
- const size_t n_data_records_)
+CTSVFile::
+CTSVFile (const string& fname_, const TSubtype subtype_, const int flags_,
+ const list<SChannel>& channels_,
+ const size_t samplerate_,
+ const double recording_time_)
: CSource (fname_, flags_),
- data_record_size (data_record_size_),
- n_data_records (n_data_records_),
- _subtype (subtype_)
+ _subtype (subtype_),
+ _samplerate (samplerate_)
{
_fd = open( fname_.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP);
if ( _fd == -1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument ("CEDFFile::CEDFFile(): file open error");
- }
-
- header_length = 256 + (channels_.size() * 256);
- size_t total_samplerate = 0;
- for ( auto& H : channels_ )
- total_samplerate += H.second; // total samplerate
-
- _fsize = header_length + 2 * total_samplerate * data_record_size * n_data_records;
- // extend
- if ( lseek( _fd, _fsize-1, SEEK_SET) == -1 || write( _fd, "\0", 1) != 1 ) {
- _status |= TStatus::sysfail;
- throw invalid_argument ("CEDFFile::CEDFFile(): file write error");
- }
-
-// size_t sys_page_size = (size_t) sysconf( _SC_PAGESIZE);
- _mmapping =
- mmap( NULL,
- _fsize,
- PROT_READ | PROT_WRITE, MAP_SHARED,
- _fd,
- 0);
- if ( _mmapping == (void*)-1 ) {
- close( _fd);
- throw length_error ("CEDFFile::CEDFFile(): mmap error");
+ fprintf( stderr, "CTSVFile::CTSVFile(\"%s\"): Failed to open file for writing\n", fname_.c_str());
+ throw invalid_argument (explain_status(_status |= TStatus::sysfail));
}
// fill out some essential header fields
- channels.resize( channels_.size());
- _lay_out_header();
+ resize_seconds( recording_time_);
- strncpy( header.version_number, version_string, 8);
_subject.id = "Fafa_1";
set_recording_id( "Zzz");
set_comment( fname_);
@@ -331,7 +142,7 @@ CEDFFile (const string& fname_, const TSubtype subtype_, const int flags_,
void
-CEDFFile::SSignal::
+CTSVFile::SSignal::
set_physical_range( const double m, const double M)
{
strncpy( header.physical_min, pad( to_string( physical_min = m), 8).c_str(), 8);
@@ -340,7 +151,7 @@ set_physical_range( const double m, const double M)
void
-CEDFFile::SSignal::
+CTSVFile::SSignal::
set_digital_range( const int16_t m, const int16_t M)
{
strncpy( header.digital_min, pad( to_string( digital_min = m), 8).c_str(), 8);
@@ -353,7 +164,7 @@ set_digital_range( const int16_t m, const int16_t M)
// uncomment on demand (also un-dnl AC_CHECK_FUNCS(mremap,,) in configure.ac)
/*
size_t
-CEDFFile::
+CTSVFile::
resize( const size_t new_records)
{
size_t total_samples_per_record = 0;
@@ -384,7 +195,7 @@ resize( const size_t new_records)
if ( _mmapping == (void*)-1 ) {
close( _fd);
- throw length_error ("CEDFFile::resize(): mmap error");
+ throw length_error ("CTSVFile::resize(): mmap error");
}
_fsize = new_fsize;
@@ -393,8 +204,8 @@ resize( const size_t new_records)
*/
-CEDFFile::
-CEDFFile (CEDFFile&& rv)
+CTSVFile::
+CTSVFile (CTSVFile&& rv)
: CSource (move(rv))
{
header = rv.header; // no need to re-layout as we don't mremap
@@ -420,12 +231,12 @@ CEDFFile (CEDFFile&& rv)
_mmapping = rv._mmapping;
_fd = rv._fd;
- rv._mmapping = (void*)-1; // will prevent munmap in ~CEDFFile()
+ rv._mmapping = (void*)-1; // will prevent munmap in ~CTSVFile()
}
-CEDFFile::
-~CEDFFile ()
+CTSVFile::
+~CTSVFile ()
{
if ( _mmapping != (void*)-1 ) {
munmap( _mmapping, _fsize);
@@ -442,7 +253,7 @@ CEDFFile::
void
-CEDFFile::
+CTSVFile::
write_ancillary_files()
{
for ( auto &I : channels ) {
@@ -478,63 +289,8 @@ write_ancillary_files()
-
-void
-CEDFFile::
-_lay_out_header()
-{
- header.version_number = (char*)_mmapping; //[ 8],
- header.patient_id = header.version_number + 8; //[80],
- header.recording_id = header.patient_id + 80; //[80],
- header.recording_date = header.recording_id + 80; //[ 8],
- header.recording_time = header.recording_date + 8; //[ 8],
- header.header_length = header.recording_time + 8; //[ 8],
- header.reserved = header.header_length + 8; //[44],
- header.n_data_records = header.reserved + 44; //[ 8],
- header.data_record_size = header.n_data_records + 8; //[ 8],
- header.n_channels = header.data_record_size + 8; //[ 4];
-
- char *p = (char*)_mmapping + 256;
- size_t h;
- vector<SSignal>::iterator H;
-#define FOR(A, C) \
- for ( h = 0, H = channels.begin(); H != channels.end(); ++h, ++H, p += C ) H->A = p;
-
- FOR (header.label, 16);
- FOR (header.transducer_type, 80);
- FOR (header.physical_dim, 8);
- FOR (header.physical_min, 8);
- FOR (header.physical_max, 8);
- FOR (header.digital_min, 8);
- FOR (header.digital_max, 8);
- FOR (header.filtering_info, 80);
- FOR (header.samples_per_record, 8);
- FOR (header.reserved, 32);
-#undef FOR
-}
-
-
-
-
-char*
-CEDFFile::
-_get_next_field( char *&field, const size_t fld_size) throw (TStatus)
-{
- if ( _fld_pos + fld_size > _fsize ) {
- _status |= bad_header;
- throw bad_header;
- }
-
- field = (char*)_mmapping + _fld_pos;
- _fld_pos += fld_size;
-
- return field;
-}
-
-size_t CEDFFile::max_channels = 256;
-
int
-CEDFFile::
+CTSVFile::
_parse_header()
{
size_t n_channels;
@@ -825,7 +581,7 @@ outer_break:
int
-CEDFFile::
+CTSVFile::
_extract_embedded_annotations()
{
auto S = find( channels.begin(), channels.end(), sigfile::edf_annotations_label);
@@ -899,7 +655,7 @@ _extract_embedded_annotations()
string
-CEDFFile::
+CTSVFile::
details( const int which) const
{
ostringstream recv;
@@ -994,7 +750,7 @@ details( const int which) const
string
-CEDFFile::
+CTSVFile::
explain_edf_status( const int status)
{
list<string> recv;
diff --git a/src/libsigfile/edf.hh b/src/libsigfile/tsv.hh
similarity index 57%
copy from src/libsigfile/edf.hh
copy to src/libsigfile/tsv.hh
index c917f29..9152c47 100644
--- a/src/libsigfile/edf.hh
+++ b/src/libsigfile/tsv.hh
@@ -1,16 +1,16 @@
/*
- * File name: libsigfile/edf.hh
+ * File name: libsigfile/tsv.hh
* Project: Aghermann
* Author: Andrei Zavada <johnhommer at gmail.com>
- * Initial version: 2008-07-01
+ * Initial version: 2013-06-22
*
- * Purpose: EDF class, also accommodating EDF+
+ * Purpose: plain ascii (tsv) data source
*
* License: GPL
*/
-#ifndef _SIGFILE_EDF_H
-#define _SIGFILE_EDF_H
+#ifndef AGH_SIGFILE_TSV_H_
+#define AGH_SIGFILE_TSV_H_
#include <cinttypes>
#include <cstring>
@@ -38,19 +38,18 @@ namespace sigfile {
-class CEDFFile
+class CTSVFile
: public CSource {
// deleted
- bool operator==( const CEDFFile&) const = delete;
- CEDFFile() = delete;
+ bool operator==( const CTSVFile&) const = delete;
+ CTSVFile() = delete;
public:
// subtype
enum TSubtype {
- edf,
- edfplus_c, // continuous
- edfplus_d // discontinuous
+ csv,
+ tsv,
};
TSubtype subtype() const
{ return _subtype; }
@@ -58,10 +57,9 @@ class CEDFFile
subtype_s( TSubtype t)
{
switch (t) {
- case edf: return "edf";
- case edfplus_c: return "edf+c";
- case edfplus_d: return "edf+d";
- default: return "(invalid)";
+ case csv: return "csv";
+ case tsv: return "tsv";
+ default: return "(invalid)";
}
}
const char*
@@ -69,45 +67,41 @@ class CEDFFile
{ return subtype_s( _subtype); }
// ctor
- CEDFFile( const CEDFFile&)
+ CTSVFile( const CTSVFile&)
: CSource("")
{
throw invalid_argument("nono");
}
enum TFlags {
- no_mmap = 1<<3,
- no_cache = 1<<4, // just considering
no_field_consistency_check = 1<<5,
};
// open existing
- CEDFFile (const string& fname, int flags = 0);
+ CTSVFile (const string& fname, int flags = 0);
// create new
- CEDFFile (const string& fname, TSubtype subtype_, int flags,
+ CTSVFile (const string& fname, TSubtype, int flags,
const list<pair<SChannel, size_t>>& channels,
size_t data_record_size = 1,
size_t n_data_records = 0);
- CEDFFile (CEDFFile&& rv);
+ CTSVFile (CTSVFile&& rv);
// dtor
- ~CEDFFile ();
+ ~CTSVFile ();
// interface
// status
string explain_status() const
- { return explain_edf_status( _status); }
+ { return explain_status( _status); }
// identification
- const char* filename() const
- { return _filename.c_str(); }
const char* patient_id() const
- { return _patient_id.c_str(); }
+ { return header.patient_id.c_str(); }
const char* recording_id() const
- { return header.recording_id; }
+ { return header.recording_id.c_str(); }
const char* comment() const
- { return header.reserved; }
+ { return header.comment.c_str(); }
const char* episode() const
- { return _episode.c_str(); }
+ { return header._episode.c_str(); }
const char* session() const
- { return _session.c_str(); }
+ { return header._session.c_str(); }
// times
time_t start_time() const
@@ -115,18 +109,37 @@ class CEDFFile
time_t end_time() const
{ return _end_time; }
double recording_time() const // in seconds
- { return n_data_records * data_record_size; }
+ { return channels.front().data.size() * _samplerate; } // all channels have the same sr, obviously
// setters
- int set_patient_id( const string&);
- int set_recording_id( const string&);
- int set_episode( const string&);
- int set_session( const string&);
- int set_reserved( const string&);
+ int set_patient_id( const string& s)
+ {
+ header.patient_id = s;
+ return 0;
+ }
+ int set_recording_id( const string& s)
+ {
+ header.recording_id = s;
+ return 0;
+ }
+ int set_episode( const string& s) // assigning to _episode or _session directly won't have a lasting effect; think again.
+ {
+ header._episode = s;
+ return 0;
+ }
+ int set_session( const string& s)
+ {
+ header._session = s;
+ return 0;
+ }
int set_comment( const string& s)
- { return set_reserved( s); }
+ {
+ header.comment = s;
+ return 0;
+ }
int set_start_time( time_t);
+
// channels
size_t n_channels() const
{ return channels.size(); }
@@ -158,8 +171,8 @@ class CEDFFile
{ return operator[](h).ucd.type(); }
size_t
- samplerate( const int h) const
- { return operator[](h).samples_per_record / data_record_size; }
+ samplerate( int) const
+ { return _samplerate; }
list<SAnnotation>&
annotations( const int h)
@@ -198,26 +211,21 @@ class CEDFFile
valarray<TFloat>
get_signal_original( const int h) const // there is a CSource::get_signal_original already, but this one is a little better
- { return get_region_original_smpl(
- h, 0, n_data_records * operator[](h).samples_per_record); }
+ { return get_region_original_smpl( h, 0, channels.front().data.size()); }
valarray<TFloat>
get_region_filtered_smpl( int, size_t, size_t) const;
valarray<TFloat>
get_signal_filtered( const int h) const
- { return get_region_filtered_smpl(
- h, 0, n_data_records * operator[](h).samples_per_record); }
+ { return get_region_filtered_smpl( h, 0, channels.front().data.size()); }
// put signal
- int
- put_region_smpl( int, const valarray<TFloat>&, size_t) const;
- int
- put_region_sec( const int h, const valarray<TFloat>& src, const float offset) const
- { return put_region_smpl( h, src, (size_t)(offset * samplerate(h))); }
+ int put_region_smpl( int, const valarray<TFloat>&, size_t) const;
+ int put_region_sec( const int h, const valarray<TFloat>& src, const float offset) const
+ { return put_region_smpl( h, src, (size_t)(offset * _samplerate)); }
- int
- put_signal( const int h, const valarray<TFloat>& src) const
+ int put_signal( const int h, const valarray<TFloat>& src) const
{ return put_region_smpl( h, src, 0); }
// signal data info
@@ -229,11 +237,6 @@ class CEDFFile
}
pair<TFloat, TFloat>
- get_max_original_signal_range( const int h) const
- { return {(TFloat)channels[h].digital_min, (TFloat)channels[h].digital_max}; }
-
-
- pair<TFloat, TFloat>
get_real_filtered_signal_range( const int h) const
{
auto x = get_signal_filtered( h);
@@ -249,79 +252,49 @@ class CEDFFile
// adjust capacity
size_t
- resize( size_t new_records);
+ resize_seconds( double);
// export
- int
- export_original( int h, const string& fname) const;
- int
- export_filtered( int h, const string& fname) const;
+ int export_original( int h, const string& fname) const;
+ int export_filtered( int h, const string& fname) const;
- int
- export_original_( int h, const string& fname) const;
- int
- export_filtered_( int h, const string& fname) const;
+ int export_original_( int h, const string& fname) const;
+ int export_filtered_( int h, const string& fname) const;
// reporting & misc
void write_ancillary_files();
- enum TEdfDetails { with_channels = 1, with_annotations = 2 };
+ enum TTsvDetails { with_channels = 1, with_annotations = 2 };
string details( int which) const;
sigproc::TWinType af_dampen_window_type; // master copy
- // static fields (mmapped)
- struct SEDFHeader {
- char *version_number , //[ 8],
- *patient_id , //[80], // maps to subject name
- *recording_id , //[80], // maps to episode_name (session_name)
- *recording_date , //[ 8],
- *recording_time , //[ 8],
- *header_length , //[ 8],
- *reserved , //[44],
- *n_data_records , //[ 8],
- *data_record_size, //[ 8],
- *n_channels ; //[ 4];
+ // header
+ struct SHeader {
+ string patient_id,
+ recording_id,
+ recording_date,
+ recording_time,
+ comment;
+
+ string _episode,
+ _session;
};
- SEDFHeader header;
+ SHeader header;
- // (relevant converted integers)
- double data_record_size;
- size_t n_data_records;
+ map<string,string>
+ metadata;
// channels
struct SSignal {
- static const char* edf_annotations_label;
- struct SEDFSignalHeader {
- char *label ,// [16],
- *transducer_type ,// [80],
- *physical_dim ,// [ 8],
- *physical_min ,// [ 8],
- *physical_max ,// [ 8],
- *digital_min ,// [ 8],
- *digital_max ,// [ 8],
- *filtering_info ,// [80],
- *samples_per_record,// [ 8],
- *reserved ;// [32];
- };
- SEDFSignalHeader
- header;
SChannel
ucd; // Universal Channel Designation, епта
- string transducer_type,
- physical_dim,
- filtering_info,
- reserved;
-
- int digital_min,
- digital_max;
- double physical_min,
- physical_max,
- scale;
- void set_physical_range( double, double);
- void set_digital_range( int16_t, int16_t);
- size_t samples_per_record;
+
+ double scale;
+
+ valarray<TFloat>
+ data;
bool operator==( const SChannel& h) const
{
@@ -338,13 +311,9 @@ class CEDFFile
artifacts;
SFilterPack
filters;
- private:
- friend class CEDFFile;
- size_t _at; // offset of our chunk within record, in samples
};
vector<SSignal>
channels;
- static size_t max_channels;
list<SAnnotation> // timepoints in seconds
common_annotations;
@@ -382,9 +351,7 @@ class CEDFFile
enum TStatus : int_least32_t {
ok = 0,
bad_header = (1 << 0),
- bad_version = (1 << 1),
bad_numfld = (1 << 2),
- bad_recording = (1 << 3),
date_unparsable = (1 << 4),
time_unparsable = (1 << 5),
nosession = (1 << 6),
@@ -392,57 +359,32 @@ class CEDFFile
nonkemp_signaltype = (1 << 8),
non1020_channel = (1 << 9),
dup_channels = (1 << 10),
- nogain = (1 << 11),
sysfail = (1 << 12),
too_many_channels = (1 << 13),
nonconforming_patient_id = (1 << 14),
missing_patient_id = (1 << 15),
invalid_subject_details = (1 << 16),
- file_truncated = (1 << 17),
- trailing_junk = (1 << 18),
extra_patientid_subfields = (1 << 19),
- recognised_channel_conflicting_type = (1 << 20),
inoperable = (bad_header
- | bad_version
| bad_numfld
- | bad_recording
| date_unparsable | time_unparsable
| dup_channels
- | nogain
| sysfail
- | too_many_channels
- | file_truncated)
+ | too_many_channels)
};
- static string explain_edf_status( int);
+ static string explain_status( int);
private:
TSubtype _subtype;
+ size_t _samplerate;
time_t _start_time,
_end_time;
- string _patient_id, // this is trimmed, raw; parsed into SSubjectId fields
- // take care of file being named 'episode-1.edf'
- _episode,
- // loosely/possibly also use RecordingID as session
- _session;
-
- void _lay_out_header();
- int _parse_header();
- int _extract_embedded_annotations();
-
- size_t header_length,
- _fsize,
- _fld_pos,
- _total_samples_per_record;
- char* _get_next_field( char*&, size_t) throw (TStatus);
-
- void *_mmapping;
int _fd;
- vector<double>
- _record_offsets;
+ int _parse_header();
};
--
Sleep experiment manager
More information about the debian-med-commit
mailing list