|
1 | 1 |
|
2 | | -function [Data, SF, deviceID] = readGenericCSV(PATH) |
| 2 | +function [Data, SF, deviceID, devType] = readGenericCSV(PATH) |
3 | 3 | % readGenericCSV Read generic CSV files with specified format and ISO8601 timestamp |
4 | 4 | % |
5 | 5 | % |
|
13 | 13 | % The acceleration axis must conform to the standard ActiPASS AX3 |
14 | 14 | % orientation (i.e. x-axis pointing down, z-axis inwards towards skin). |
15 | 15 | % |
16 | | -% The first four lines in the CSV file must contain the |
17 | | -% deviceID (only numeric), the sampling frequency, start-time and the column names. |
18 | | -% ID=<numeric devideID> (e.g. 'ID=34567850') |
19 | | -% SF=<sampling frequency> (e.g. 'SF=50') |
20 | | -% START=<start time in ISO8601 format> (e.g. 'START=20240301T154517.250') |
21 | | -% x, y, z |
| 16 | +% The first lines in the CSV file must contain the: |
| 17 | +% * deviceID (only numeric) |
| 18 | +% * the sampling frequency (numeric) |
| 19 | +% * start-time (ISO8601 format) |
| 20 | +% * optional device-type. Allow device-type specific changes to algorithms. See supported device-types below |
| 21 | +% * and the column names x, y, and z. |
| 22 | +% |
| 23 | +% See example header lines and first data line below |
| 24 | +% |
| 25 | +% ID=34567850 |
| 26 | +% DevType=Axivity |
| 27 | +% SF=50 |
| 28 | +% START=20240301T154517.250 |
| 29 | +% x,y,z |
22 | 30 | % |
23 | 31 | % Remarks: |
24 | 32 | % 1. To improve performance and to reduce file sizes a seperate time column is excluded. |
25 | 33 | % 2. Consequently, only supports data which are resampled to a fixed sampling frequency. |
| 34 | +% |
| 35 | +% Supported Device type strings: |
| 36 | +% Axivity, ActivPAL3, ActivPAL4, Actigraph, SENS, Movisens |
26 | 37 | % |
27 | 38 | % |
28 | 39 | % Output: |
29 | 40 | % Data [Nx4] datetime (Matlab datenum format) and 3D acceleration data |
30 | 41 | % SF [double] the sample frequency |
31 | 42 | % deviceID [double] the device ID |
| 43 | +% devType [string] the device type. Set to "Generic" if not specified in CSV file |
32 | 44 |
|
33 | 45 | % Copyright (c) 2024, Claas Lendt & Pasan Hettiarachchi |
34 | 46 | % All rights reserved. |
|
59 | 71 | Data = []; |
60 | 72 | SF = NaN; |
61 | 73 | deviceID = NaN; |
| 74 | +devType=""; |
62 | 75 |
|
63 | 76 | try |
| 77 | + |
64 | 78 | % Open the file once and keep it open for reading both metadata and data |
65 | 79 | fileID = fopen(PATH, 'r'); |
66 | 80 |
|
67 | | - % Read the first three lines for metadata and start-time |
68 | | - idLine = fgetl(fileID); |
69 | | - sfLine = fgetl(fileID); |
70 | | - startLine = fgetl(fileID); |
| 81 | + % a string array to store header lines |
| 82 | + headLs=strings(10,1); |
| 83 | + headerFound=false; |
71 | 84 |
|
72 | | - % read the column header line |
73 | | - headL=fgetl(fileID); |
74 | | - |
75 | | - % Extract ID and SF values using correct indexing |
76 | | - deviceID = extractBetween(idLine,textBoundary+"ID=",textBoundary); |
77 | | - SF = extractBetween(sfLine,textBoundary+"SF=",textBoundary); |
| 85 | + % Read first few (max 10) lines for metadata and start-time (read max 10 lines) |
| 86 | + for itr=1:10 |
| 87 | + headLs(itr)=fgetl(fileID); |
| 88 | + % if the axes header line is found we have reached end of header lines |
| 89 | + if matches(headLs(itr),["x,y,z","x, y, z"],'IgnoreCase',true) |
| 90 | + headerFound=true; |
| 91 | + break; |
| 92 | + end |
| 93 | + end |
78 | 94 |
|
79 | | - % find the start time. If this fails function will return |
80 | | - startT=datenum(extractBetween(startLine,textBoundary+"START=",textBoundary),'yyyymmddTHHMMSS.FFF'); |
| 95 | + if ~headerFound |
| 96 | + error("unrecognized generic CSV format"); |
| 97 | + end |
| 98 | + |
| 99 | + row_DevID = startsWith(headLs,"ID=",'IgnoreCase',true); |
| 100 | + row_SF = startsWith(headLs,"SF=",'IgnoreCase',true); |
| 101 | + row_startT = startsWith(headLs,"START=",'IgnoreCase',true); |
| 102 | + row_devtype = startsWith(headLs,"DevType=",'IgnoreCase',true); |
81 | 103 |
|
82 | | - % check for file-validity by checking for DeviceID and SF |
83 | | - if isempty(deviceID) || isempty(SF) || isempty(startT) || ~matches(headL,["x,y,z","x, y, z"],'IgnoreCase',true) |
| 104 | + % check for file-validity by checking for DeviceID and SF |
| 105 | + if isempty(headLs(row_DevID)) || isempty(headLs(row_SF)) || isempty(headLs(row_startT)) |
84 | 106 | error("unrecognized generic CSV format"); |
85 | 107 | end |
86 | 108 |
|
| 109 | + % if a device-type header line found use it, otherwise set devType to "generic" |
| 110 | + if ~isempty(headLs(row_devtype)) |
| 111 | + devType=extractBetween(headLs(row_devtype),textBoundary+"DevType=",textBoundary); |
| 112 | + else |
| 113 | + devType="Generic"; |
| 114 | + end |
| 115 | + |
| 116 | + % Extract ID and SF values using correct indexing |
| 117 | + deviceID = extractBetween(headLs(row_DevID),textBoundary+"ID=",textBoundary); |
| 118 | + SF = extractBetween(headLs(row_SF),textBoundary+"SF=",textBoundary); |
| 119 | + |
| 120 | + % find the start time. If this fails function will return |
| 121 | + startT=datenum(extractBetween(headLs(row_startT),textBoundary+"START=",textBoundary),'yyyymmddTHHMMSS.FFF'); |
| 122 | + |
87 | 123 | % convert SF and DeviceID to numbers (ActiPASS only supports numeric device serial-numbers) |
88 | 124 | SF=str2double(SF); |
89 | 125 | deviceID=str2double(deviceID); |
|
0 commit comments