Import upstream version 0.1.3+git20210707.1.39e9cfb
Debian Janitor
2 years ago
0 | name: CMake | |
1 | ||
2 | on: | |
3 | push: | |
4 | branches: [ master ] | |
5 | pull_request: | |
6 | branches: [ master ] | |
7 | ||
8 | env: | |
9 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) | |
10 | BUILD_TYPE: Release | |
11 | ||
12 | jobs: | |
13 | build: | |
14 | # The CMake configure and build commands are platform agnostic and should work equally | |
15 | # well on Windows or Mac. You can convert this to a matrix build if you need | |
16 | # cross-platform coverage. | |
17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix | |
18 | runs-on: ubuntu-latest | |
19 | ||
20 | steps: | |
21 | - uses: actions/checkout@v2 | |
22 | ||
23 | - name: Install Dependencies | |
24 | run: sudo apt-get update && sudo apt-get install -yq libboost-all-dev | |
25 | ||
26 | - name: Configure CMake | |
27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. | |
28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type | |
29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTING=yes | |
30 | ||
31 | - name: Build | |
32 | # Build your program with the given configuration | |
33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} | |
34 | ||
35 | - name: Test | |
36 | working-directory: ${{github.workspace}}/build | |
37 | # Execute tests defined by the CMake configuration. | |
38 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail | |
39 | run: ctest -C ${{env.BUILD_TYPE}} | |
40 |
0 | ||
1 | os: linux | |
2 | arch: | |
3 | - amd64 | |
4 | - ppc64le | |
5 | language: cpp | |
6 | ||
7 | compiler: | |
8 | - gcc | |
9 | - clang | |
10 | ||
11 | before_install: | |
12 | - sudo apt-get update -qq | |
13 | - sudo apt-get install libboost-all-dev | |
14 | ||
15 | script: | |
16 | - mkdir build | |
17 | - cd build | |
18 | - cmake .. -DBUILD_TESTING=yes | |
19 | - make | |
20 | - make test |
19 | 19 | |
20 | 20 | # Make sure the version is in sync with |
21 | 21 | # libgdf/include/GDF/Version.h |
22 | set(GDF_VERSION "0.1.3") | |
22 | set(GDF_VERSION "0.1.4") | |
23 | 23 | |
24 | 24 | # shared library API versioning (soversion) -- NOT the same as the release version |
25 | 25 | # it follows first number |
0 | # | |
1 | # This file is part of libGDF. | |
2 | # | |
3 | # libGDF is free software: you can redistribute it and/or modify | |
4 | # it under the terms of the GNU Lesser General Public License as | |
5 | # published by the Free Software Foundation, either version 3 of | |
6 | # the License, or (at your option) any later version. | |
7 | # | |
8 | # libGDF is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU Lesser General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU Lesser General Public License | |
14 | # along with libGDF. If not, see <http://www.gnu.org/licenses/>. | |
15 | # | |
16 | # Copyright 2010 Martin Billinger | |
17 | ||
18 | ||
19 | A. Build Instructions (out of source build) | |
20 | ||
21 | replace $GDF_ROOT with the (relative or absolute) path to the source | |
22 | tree (e.g. ~/SVN/GDF/trunk). | |
23 | ||
24 | > mkdir build | |
25 | > cd build | |
26 | > cmake $GDF_ROOT | |
27 | > make | |
28 | > make check |
0 | [![Build Status](https://travis-ci.org/mbillingr/libgdf.svg?branch=master)](https://travis-ci.com/mbillingr/libgdf) | |
1 | [![CMake](https://github.com/mbillingr/libgdf/actions/workflows/cmake.yml/badge.svg)](https://github.com/mbillingr/libgdf/actions/workflows/cmake.yml) | |
2 | ||
3 | libGDF | |
4 | ====== | |
5 | ||
6 | C++ implementation of GDF - "a general dataformat for biosignals" version V2.20. | |
7 | ||
8 | Obtaining libGDF | |
9 | ---------------- | |
10 | ||
11 | Use the following command to fetch the sources: | |
12 | ||
13 | git clone https://github.com/kazemakase/libgdf.git libgdf | |
14 | ||
15 | Dependencies | |
16 | ------------ | |
17 | Required: boost | |
18 | ||
19 | Build Instructions | |
20 | ------------------ | |
21 | ||
22 | The preferred method is to perform an out-of-source build. | |
23 | Replace `$GDF_ROOT` with the (relative or absolute) path to the source | |
24 | tree (e.g. ~/repositories/libgdf). | |
25 | ||
26 | mkdir build | |
27 | cd build | |
28 | cmake $GDF_ROOT -DBUILD_TESTING=yes | |
29 | make | |
30 | make test |
9 | 9 | epoch= |
10 | 10 | pkgdesc="C++ implementation of GDF, the general dataformat for biosignals" |
11 | 11 | arch=('i686' 'x86_64') |
12 | url="http://sourceforge.net/projects/libgdf/" | |
12 | url="http://github.com/kazemakase/libgdf/" | |
13 | 13 | license=('LGPL') |
14 | 14 | groups=() |
15 | 15 | depends=(boost-libs) |
27 | 27 | noextract=() |
28 | 28 | md5sums=() #generate with 'makepkg -g' |
29 | 29 | |
30 | _gitroot='git://libgdf.git.sourceforge.net/gitroot/libgdf/libgdf' | |
30 | _gitroot='https://github.com/kazemakase/libgdf.git' | |
31 | 31 | _gitname='libgdf' |
32 | 32 | |
33 | 33 | build() { |
30 | 30 | /// using the flag 0x8000 which marks the type of mode 1 stop events |
31 | 31 | /// @throws general if events could not be converted |
32 | 32 | std::vector<Mode3Event> convertMode1EventsIntoMode3Events (std::vector<Mode1Event> |
33 | const& mode_1_events) | |
34 | throw (exception::general); | |
33 | const& mode_1_events); | |
35 | 34 | } |
36 | 35 | |
37 | 36 | #endif |
124 | 124 | inline size_t getNumFullRecords( ) const { return m_num_full; } |
125 | 125 | |
126 | 126 | /// Get number of partially filled records currently in the list. |
127 | inline size_t getNumPartialRecords( ) const { return m_records.size( ); } | |
127 | inline size_t getNumPartialRecords( ) const { return m_num_recs; } | |
128 | 128 | |
129 | 129 | /// Returns reference to channel specified by channel_idx |
130 | 130 | /** If channel does not exist gdf::nonexistent_channel_access::nonexistent_channel_access is thrown. |
144 | 144 | PointerPool<Record> *m_pool; |
145 | 145 | std::list< Record* > m_records; |
146 | 146 | std::list< Record* > m_records_full; |
147 | size_t m_num_full; | |
147 | size_t m_num_full, m_num_recs; | |
148 | 148 | std::vector< std::list< Record* >::iterator > m_channelhead; |
149 | 149 | std::list<RecordFullHandler*> m_recfull_callbacks; |
150 | 150 | }; |
35 | 35 | { |
36 | 36 | public: |
37 | 37 | /// Constructor |
38 | TagField( uint8 tag ); | |
38 | TagField( uint8 tag=0 ); | |
39 | 39 | |
40 | 40 | /// Destructor |
41 | 41 | virtual ~TagField( ); |
20 | 20 | |
21 | 21 | #include "Exceptions.h" |
22 | 22 | #include <boost/cstdint.hpp> |
23 | #include <boost/detail/endian.hpp> | |
23 | #include <boost/predef/other/endian.h> | |
24 | 24 | #include <iostream> |
25 | 25 | |
26 | 26 | namespace gdf |
73 | 73 | template<typename T> |
74 | 74 | void writeLittleEndian( std::ostream &out, T item ) |
75 | 75 | { |
76 | #if defined(BOOST_LITTLE_ENDIAN) | |
76 | #if BOOST_ENDIAN_LITTLE_BYTE | |
77 | 77 | out.write( reinterpret_cast<const char*>(&item), sizeof(item) ); |
78 | #elif defined(BOOST_BIG_ENDIAN) | |
78 | #elif BOOST_ENDIAN_BIG_BYTE | |
79 | 79 | const char* p = reinterpret_cast<const char*>(&item) + sizeof(item)-1; |
80 | 80 | for( size_t i=0; i<sizeof(item); i++ ) |
81 | 81 | out.write( p--, 1 ); |
87 | 87 | template<typename T> |
88 | 88 | void readLittleEndian( std::istream &in, T &item ) |
89 | 89 | { |
90 | #if defined(BOOST_LITTLE_ENDIAN) | |
90 | #if BOOST_ENDIAN_LITTLE_BYTE | |
91 | 91 | in.read( reinterpret_cast<char*>(&item), sizeof(item) ); |
92 | #elif defined(BOOST_BIG_ENDIAN) | |
92 | #elif BOOST_ENDIAN_BIG_BYTE | |
93 | 93 | char* p = reinterpret_cast<char*>(&item) + sizeof(item)-1; |
94 | 94 | for( size_t i=0; i<sizeof(item); i++ ) |
95 | 95 | in.read( p--, 1 ); |
20 | 20 | |
21 | 21 | // Current library version |
22 | 22 | // Must be the same as in the top level CMakeLists.txt |
23 | #define GDF_VERSION "0.1.3" | |
23 | #define GDF_VERSION "0.1.4" | |
24 | 24 | |
25 | 25 | |
26 | 26 | #endif |
33 | 33 | |
34 | 34 | //------------------------------------------------------------------------- |
35 | 35 | vector<Mode3Event> convertMode1EventsIntoMode3Events (vector<Mode1Event> const& mode_1_events) |
36 | throw (exception::general) | |
37 | 36 | { |
38 | 37 | vector<Mode3Event> mode_3_events; |
39 | 38 |
195 | 195 | { |
196 | 196 | desc = ""; |
197 | 197 | int i =0; |
198 | while( value[pos+i] != 0 && (pos+i < tagfieldlength)) { | |
198 | while( (pos + i < tagfieldlength) && value[pos+i] != 0 ) { | |
199 | 199 | desc.push_back(value[pos+i]); |
200 | 200 | i++; |
201 | 201 | } |
17 | 17 | |
18 | 18 | #include "GDF/EventHeader.h" |
19 | 19 | #include "GDF/Exceptions.h" |
20 | #include <math.h> | |
20 | 21 | #include <algorithm> |
21 | 22 | #include <iostream> |
22 | 23 | #include <sstream> |
22 | 22 | #include <boost/numeric/conversion/cast.hpp> |
23 | 23 | #include <list> |
24 | 24 | #include <string> |
25 | //#include <iostream> | |
26 | ||
27 | const double epsilon_d = 1e-15; | |
28 | const double epsilon_f = 1e-15; | |
29 | const double epsilon_i = 1; | |
30 | 25 | |
31 | 26 | |
32 | 27 | namespace gdf |
402 | 397 | hdr.getTagHeader_readonly( ).toStream( out ); |
403 | 398 | uint16 header3LenBlocks = mh->get_header_length() - (1+ns); |
404 | 399 | assert( out.tellp() == std::streampos(256+256*ns+256*header3LenBlocks)); |
400 | (void)header3LenBlocks; // To prevent -Werror=unused-variable in a release build. | |
405 | 401 | |
406 | 402 | return out; |
407 | 403 | } |
416 | 412 | MainHeader *mh = &hdr.m_mainhdr; |
417 | 413 | mh->version_id.fromstream( in ); |
418 | 414 | int gdf_version_int = mh->getGdfVersionInt(); |
415 | #ifdef ALLOW_GDF_V_251 | |
416 | if (gdf_version_int < 210 || gdf_version_int > 251) | |
417 | #else | |
419 | 418 | if (gdf_version_int < 210 || gdf_version_int > 220) |
419 | #endif | |
420 | 420 | throw exception::incompatible_gdf_version (mh->get_version_id ()); |
421 | ||
421 | 422 | mh->patient_id.fromstream( in ); |
422 | 423 | mh->reserved_1.fromstream( in ); |
423 | 424 | mh->patient_drugs.fromstream( in ); |
483 | 484 | { |
484 | 485 | case 0: |
485 | 486 | break; |
487 | #ifdef ALLOW_GDF_V_251 | |
488 | default: | |
489 | evd.fromTagField(tagfield); | |
490 | taghdr.setEventDescriptor(evd); | |
491 | break; | |
492 | #else | |
486 | 493 | case 1: |
487 | 494 | evd.fromTagField(tagfield); |
488 | 495 | taghdr.setEventDescriptor(evd); |
490 | 497 | default: |
491 | 498 | throw exception::feature_not_implemented("Only tag==1 is supported in this build"); |
492 | 499 | break; |
500 | #endif | |
493 | 501 | } |
494 | 502 | } |
495 | 503 | taghdr.setLength(); |
103 | 103 | { |
104 | 104 | delete m_record_cache[i]; |
105 | 105 | m_record_cache[i] = NULL; |
106 | m_record_changed[i] = NULL; | |
106 | m_record_changed[i] = false; | |
107 | 107 | } |
108 | 108 | } |
109 | 109 | } |
66 | 66 | { |
67 | 67 | size_t samplesize = datatype_size( m_header.getSignalHeader_readonly( i ).get_datatype( ) ); |
68 | 68 | m_record_length += samplesize * m_header.getSignalHeader_readonly( i ).get_samples_per_record( ); |
69 | ||
69 | #ifdef ALLOW_GDF_V_251 | |
70 | double fs = m_header.getSignalHeader( i ).get_samples_per_record( ); | |
71 | #else | |
70 | 72 | double fs = m_header.getSignalHeader( i ).get_samples_per_record( ) * m_header.getMainHeader_readonly().get_datarecord_duration(1) / m_header.getMainHeader_readonly().get_datarecord_duration(0); |
73 | #endif | |
71 | 74 | m_header.getSignalHeader( i ).set_samplerate( boost::numeric_cast<uint32>(fs) ); |
72 | 75 | } |
73 | 76 | |
144 | 147 | } |
145 | 148 | |
146 | 149 | buffer.resize( signal_indices.size() ); |
147 | ||
150 | #ifdef ALLOW_GDF_V_251 | |
151 | double record_rate = 1; | |
152 | #else | |
148 | 153 | double record_rate = m_header.getMainHeader_readonly().get_datarecord_duration(1) / m_header.getMainHeader_readonly().get_datarecord_duration(0); |
154 | #endif | |
149 | 155 | size_t record = boost::numeric_cast<size_t>( floor( start_time * record_rate ) ); |
150 | 156 | |
151 | 157 |
58 | 58 | m_records.clear( ); |
59 | 59 | m_records_full.clear( ); |
60 | 60 | |
61 | m_num_recs = 0; | |
61 | 62 | m_num_full = 0; |
62 | 63 | } |
63 | 64 | |
96 | 97 | //std::cout << "Record Full" << std::endl; |
97 | 98 | m_records_full.push_back( m_records.front() ); |
98 | 99 | m_records.pop_front( ); |
100 | m_num_recs--; | |
99 | 101 | m_num_full++; |
100 | 102 | |
101 | 103 | // Sparse channels do not need m_channelhead[i] mechanism, but the mechanism |
193 | 195 | Record *r = m_pool->pop( ); |
194 | 196 | r->clear( ); |
195 | 197 | m_records.push_back( r ); |
198 | m_num_recs++; | |
196 | 199 | std::list< Record* >::iterator it = m_records.end( ); |
197 | 200 | it--; |
198 | 201 | return it; |
229 | 232 | |
230 | 233 | if( m_channelhead[channel_idx] == m_records.end() ) |
231 | 234 | { |
232 | if( m_records.size() > 0 ) | |
235 | if( m_num_recs > 0 ) | |
233 | 236 | { |
234 | 237 | if( m_records.back()->getChannel(channel_idx)->getFree( ) == 0 ) |
235 | { | |
236 | // Create a new record in m_records and inform all | |
237 | // channels that are pointing beyond the end m_records. | |
238 | ||
239 | // capture the iter value that flags m_channelhead's that have no free space | |
240 | std::list< Record* >::iterator end_iter = m_records.end(); | |
241 | // get a clean record from m_pool, enlist it on m_records, and return an iterator | |
242 | std::list< Record* >::iterator newrec_iter = createNewRecord( ); | |
243 | // broadcast the new record among all channels that need it | |
244 | for( size_t i=0; i<m_channelhead.size(); i++ ) | |
245 | { | |
246 | if (m_channelhead[i] == end_iter) | |
247 | { | |
248 | m_channelhead[i] = newrec_iter; | |
249 | } | |
250 | } | |
251 | } | |
238 | { | |
239 | // Create a new record in m_records and inform all | |
240 | // channels that are pointing beyond the end m_records. | |
241 | ||
242 | // capture the iter value that flags m_channelhead's that have no free space | |
243 | std::list< Record* >::iterator end_iter = m_records.end(); | |
244 | // get a clean record from m_pool, enlist it on m_records, and return an iterator | |
245 | std::list< Record* >::iterator newrec_iter = createNewRecord( ); | |
246 | // broadcast the new record among all channels that need it | |
247 | for( size_t i=0; i<m_channelhead.size(); i++ ) | |
248 | { | |
249 | if (m_channelhead[i] == end_iter) | |
250 | { | |
251 | m_channelhead[i] = newrec_iter; | |
252 | } | |
253 | } | |
254 | } | |
252 | 255 | else |
253 | 256 | throw exception::corrupt_recordbuffer( "DOOM is upon us!" ); |
254 | 257 | } |
55 | 55 | gdf::TagField tagfield(0); |
56 | 56 | tagfield.fromStream(stream); |
57 | 57 | tag = tagfield.getTagNumber(); |
58 | #ifdef ALLOW_GDF_V_251 | |
59 | if (tag == 0) | |
60 | { | |
61 | // Zero tag value indicates end of Header 3. See first row of Table 10 in GDF standard. | |
62 | header3unpaddedsize += 1; | |
63 | } | |
64 | else if (1 <= tag && tag <= 13) | |
65 | { | |
66 | header3unpaddedsize += tagfield.getLength(); | |
67 | this->addTagField( tagfield ); | |
68 | } | |
69 | else | |
70 | { | |
71 | throw exception::feature_not_implemented("Only tag==1..13 are supported in this build"); | |
72 | } | |
73 | #else | |
58 | 74 | switch( tag ) |
59 | 75 | { |
60 | 76 | case 0: |
69 | 85 | throw exception::feature_not_implemented("Only tag==1 is supported in this build"); |
70 | 86 | break; |
71 | 87 | } |
88 | #endif | |
72 | 89 | } |
73 | 90 | // consume the padding bytes that make Header 3 a multiple of 256 bytes |
74 | 91 | size_t ns = hdr.getMainHeader_readonly().get_num_signals( ); |
184 | 201 | //=================================================================================================== |
185 | 202 | //=================================================================================================== |
186 | 203 | |
187 | TagField::TagField( uint8 tag=0 ) : m_tag(tag) | |
204 | TagField::TagField( uint8 tag ) : m_tag(tag) | |
188 | 205 | { |
189 | 206 | ; |
190 | 207 | } |
232 | 249 | for( size_t i=0; i<taglength; i++ ) |
233 | 250 | stream.read( reinterpret_cast<char*>(&m_value[i+4]), 1 ); |
234 | 251 | } |
235 | }⏎ | |
252 | } | |
253 |
15 | 15 | #ifndef __COMMANDS_H_INCLUDED__ |
16 | 16 | #define __COMMANDS_H_INCLUDED__ |
17 | 17 | |
18 | #include <map> | |
18 | 19 | #include <stdexcept> |
19 | 20 | #include <string> |
20 | 21 | #include "mex.h" |
48 | 49 | public: |
49 | 50 | CommandManager( ) |
50 | 51 | { |
52 | default_cmd = NULL; | |
51 | 53 | } |
52 | 54 | |
53 | 55 | virtual ~CommandManager( ) |
55 | 57 | std::map< std::string, Command* >::iterator it = commands.begin( ); |
56 | 58 | for( ; it != commands.end(); it++ ) |
57 | 59 | delete it->second; |
60 | } | |
61 | ||
62 | void setDefaultCommand( const std::string cmdstr ) | |
63 | { | |
64 | std::map< std::string, Command* >::iterator it = commands.find( toUpper( cmdstr ) ); | |
65 | if( it == commands.end() ) | |
66 | throw std::invalid_argument( "Invalid command: "+toUpper( cmdstr ) ); | |
67 | default_cmd = it->second; | |
58 | 68 | } |
59 | 69 | |
60 | 70 | void registerCommand( const std::string cmdstr, Command * cmd, size_t nlhs, size_t nrhs ) |
71 | 81 | (*it->second)( nlhs, plhs, nrhs, prhs ); |
72 | 82 | } |
73 | 83 | |
84 | void execute( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) | |
85 | { | |
86 | if( !default_cmd ) | |
87 | throw std::invalid_argument( "No default command specified." ); | |
88 | (*default_cmd)( nlhs, plhs, nrhs, prhs ); | |
89 | } | |
90 | ||
74 | 91 | static std::string toUpper( const std::string str ) |
75 | 92 | { |
76 | 93 | std::string out( str ); |
83 | 100 | |
84 | 101 | private: |
85 | 102 | std::map< std::string, Command* > commands; |
103 | Command *default_cmd; | |
86 | 104 | }; |
87 | 105 | |
88 | 106 | #endif // COMMANDS_H |
35 | 35 | target_link_libraries( testRWConsistency ${Boost_LIBRARIES} GDF ) |
36 | 36 | add_test( NAME testRWConsistency COMMAND testRWConsistency ) |
37 | 37 | |
38 | add_executable( testBlit testBlit.cpp ) | |
39 | target_link_libraries( testBlit ${Boost_LIBRARIES} GDF ) | |
40 | add_test( NAME testBlit COMMAND testBlit ) | |
41 | ||
38 | 42 | add_executable( testSparseSampling testSparseSampling.cpp ) |
39 | 43 | target_link_libraries( testSparseSampling ${Boost_LIBRARIES} GDF ) |
40 | 44 | add_test( NAME testSparseSampling COMMAND testSparseSampling ) |
0 | // | |
1 | // This file is part of libGDF. | |
2 | // | |
3 | // libGDF is free software: you can redistribute it and/or modify | |
4 | // it under the terms of the GNU Lesser General Public License as | |
5 | // published by the Free Software Foundation, either version 3 of | |
6 | // the License, or (at your option) any later version. | |
7 | // | |
8 | // libGDF is distributed in the hope that it will be useful, | |
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | // GNU Lesser General Public License for more details. | |
12 | // | |
13 | // You should have received a copy of the GNU Lesser General Public License | |
14 | // along with libGDF. If not, see <http://www.gnu.org/licenses/>. | |
15 | // | |
16 | // Copyright 2010, 2013 Martin Billinger, Owen Kelly | |
17 | ||
18 | ||
19 | #include "config-tests.h" | |
20 | ||
21 | #include <GDF/Writer.h> | |
22 | #include <GDF/Reader.h> | |
23 | #include <GDF/TagHeader.h> | |
24 | #include <GDF/EventDescriptor.h> | |
25 | ||
26 | #include <iostream> | |
27 | #include <stdio.h> | |
28 | #include <sys/stat.h> | |
29 | ||
30 | using namespace std; | |
31 | ||
32 | const string testfile = "test.gdf.tmp"; | |
33 | const string reffile0 = string(GDF_SOURCE_ROOT)+"/sampledata/MI128.gdf"; | |
34 | const string annotfile = string(GDF_SOURCE_ROOT)+"/sampledata/Header3Tag1.gdf"; | |
35 | const string alltypesfile = string(GDF_SOURCE_ROOT)+"/sampledata/alltypes.gdf"; | |
36 | const string eventcodefile = string(GDF_SOURCE_ROOT)+"/libgdf/eventcodes.txt"; | |
37 | ||
38 | bool fexist( std::string filename ) | |
39 | { | |
40 | std::ifstream f( filename.c_str(), std::ios_base::in ); | |
41 | if( f.fail() ) | |
42 | return false; | |
43 | f.close( ); | |
44 | return true; | |
45 | } | |
46 | ||
47 | size_t fsize( std::string filename ) | |
48 | { | |
49 | struct stat filestatus; | |
50 | stat( filename.c_str(), &filestatus ); | |
51 | return filestatus.st_size; | |
52 | } | |
53 | ||
54 | bool fcompare( std::string fileA, std::string fileB ) | |
55 | { | |
56 | std::ifstream f1( fileA.c_str(), std::ios_base::in | std::ios_base::binary ); | |
57 | std::ifstream f2( fileB.c_str(), std::ios_base::in | std::ios_base::binary ); | |
58 | ||
59 | bool state = true; | |
60 | ||
61 | size_t ofs = 0; | |
62 | while( !( f1.eof() || f2.eof() ) ) | |
63 | { | |
64 | signed char a, b; | |
65 | f1 >> a; | |
66 | f2 >> b; | |
67 | ||
68 | ||
69 | if( abs(a-b) > 1 ) // tolerate a difference of 1 due to rounding errors in digitial -> physical -> digital conversion | |
70 | { | |
71 | cout << ofs << " : " << (int)a << " ... " << (int)b << endl; | |
72 | state = false; | |
73 | } | |
74 | ||
75 | ofs++; | |
76 | ||
77 | } | |
78 | return state; | |
79 | } | |
80 | ||
81 | int main( ) | |
82 | { | |
83 | std::vector<string> infilelist; // a list of files on which to run tests | |
84 | infilelist.push_back(annotfile); | |
85 | infilelist.push_back(alltypesfile); | |
86 | infilelist.push_back(reffile0); | |
87 | ||
88 | string reffile; | |
89 | ||
90 | ||
91 | try | |
92 | { | |
93 | for(size_t file_count=0; file_count < infilelist.size(); file_count++) | |
94 | { | |
95 | reffile = infilelist[file_count]; // file to be tested in this loop iteration | |
96 | ||
97 | cout << "Creating Writer instance." << endl; | |
98 | gdf::Writer w; | |
99 | ||
100 | cout << "Creating Reader instance." << endl; | |
101 | gdf::Reader r; | |
102 | ||
103 | r.enableCache( false ); | |
104 | ||
105 | cout << "Opening '" << reffile << "' for reading." << endl; | |
106 | r.open( reffile ); | |
107 | ||
108 | cout << "Copying Header information." << endl; | |
109 | w.getMainHeader( ).copyFrom( r.getMainHeader_readonly() ); | |
110 | w.getHeaderAccess().setRecordDuration( r.getMainHeader_readonly().get_datarecord_duration( 0 ), r.getMainHeader_readonly().get_datarecord_duration( 1 ) ); | |
111 | for( size_t m=0; m<w.getMainHeader_readonly().get_num_signals(); m++ ) | |
112 | { | |
113 | w.createSignal( m, true ); | |
114 | w.getSignalHeader( m ).copyFrom( r.getSignalHeader_readonly( m ) ); | |
115 | } | |
116 | ||
117 | w.setEventMode( r.getEventHeader()->getMode() ); | |
118 | w.setEventSamplingRate( r.getEventHeader()->getSamplingRate() ); | |
119 | // Copy GDF header 3 including user-specific event description table | |
120 | gdf::TagHeader ath = r.getHeaderAccess_readonly().getTagHeader_readonly(); | |
121 | w.getHeaderAccess().getTagHeader().copyFrom( ath ); | |
122 | ||
123 | cout << "Opening '" << testfile << "' for writing." << endl; | |
124 | w.open( testfile, gdf::writer_ev_memory | gdf::writer_overwrite ); | |
125 | ||
126 | cout << "Copying data .... "; | |
127 | ||
128 | //size_t num_recs = boost::numeric_cast<size_t>( r.getMainHeader_readonly( ).get_num_datarecords( ) ); | |
129 | //for( size_t n=0; n<num_recs; n++ ) | |
130 | //{ | |
131 | // gdf::Record *rec = w.acquireRecord( ); | |
132 | // r.readRecord( n, rec ); | |
133 | // w.addRecord( rec ); | |
134 | //} | |
135 | ||
136 | std::vector< std::vector< double > > buffer; | |
137 | r.getSignals(buffer); | |
138 | ||
139 | for( size_t ch=0; ch<buffer.size(); ch++) | |
140 | { | |
141 | cout << ch << endl; | |
142 | w.blitSamplesPhys(ch, buffer[ch]); | |
143 | } | |
144 | ||
145 | cout << "OK" << endl; | |
146 | ||
147 | cout << "Copying events .... "; | |
148 | gdf::EventHeader* ev_header = r.getEventHeader(); | |
149 | unsigned int num_events = ev_header->getNumEvents(); | |
150 | switch( ev_header->getMode() ) | |
151 | { | |
152 | default: throw(std::runtime_error("ERROR -- Invalid event mode!")); | |
153 | case 1: { | |
154 | gdf::Mode1Event ev; | |
155 | for(unsigned int m = 0; m < num_events; m++) | |
156 | { | |
157 | ev_header->getEvent(m, ev); | |
158 | w.addEvent(ev); | |
159 | } | |
160 | } break; | |
161 | case 3: { | |
162 | gdf::Mode3Event ev; | |
163 | double sample_physical_value; | |
164 | double sample_time_sec; | |
165 | ||
166 | // Copy all event from source file to target file. | |
167 | // Mode 1 and 3 events are copied. | |
168 | // Sparse samples are extracted to (time,phys) then stored again. | |
169 | for(unsigned int mm = 0; mm < num_events; mm++) | |
170 | { | |
171 | ev_header->getEvent(mm, ev); | |
172 | if( ev.type != 0x7fff ) { | |
173 | w.addEvent(ev); | |
174 | } else { | |
175 | r.eventToSample(sample_time_sec, sample_physical_value, ev); | |
176 | // At this point we have successfully decoded a sparse sample | |
177 | // (sample_time_sec, sample_physical_value) . | |
178 | w.sampleToEvent( sample_time_sec, sample_physical_value, ev.channel, ev ); | |
179 | // At this point we have successfully encoded a sparse sample into an event. | |
180 | // Now write the event to file. | |
181 | w.addEvent( ev ); | |
182 | } | |
183 | } | |
184 | } break; | |
185 | } | |
186 | ||
187 | cout << "OK" << endl; | |
188 | ||
189 | w.close( ); | |
190 | cout << "Comparing files .... "; | |
191 | if( !fcompare( reffile, testfile ) ) | |
192 | { | |
193 | cout << "Failed." << endl; | |
194 | return 1; | |
195 | } | |
196 | cout << "OK" << endl; | |
197 | ||
198 | cout << "Removing " << testfile << endl << endl; | |
199 | remove( testfile.c_str() ); | |
200 | } | |
201 | return 0; // test succeeded | |
202 | } | |
203 | catch( std::exception &e ) | |
204 | { | |
205 | std::cout << "Caught Exception: " << e.what( ) << endl; | |
206 | } | |
207 | catch( ... ) | |
208 | { | |
209 | std::cout << "Caught Unknown Exception." << endl; | |
210 | } | |
211 | ||
212 | return 1; // test failed | |
213 | } |