Commit 6077f2c6 authored by Marian Dovgialo's avatar Marian Dovgialo

#37290 Merge branch 'development' into 'master'

Newest version

Development

See merge request !11
parents 6c0f7a23 8c082136
Pipeline #5364 passed with stages
in 1 minute and 52 seconds
......@@ -23,6 +23,7 @@ openbci.pyproj
openbci.sln
.vs/
.idea/
obci_readmanager.egg-info/
coverage_html/
......
......@@ -31,7 +31,7 @@ test:
image: obci-image:u1604
stage: test
before_script:
- pip3 install -e .
- pip3 install -e .[test]
- apt-get -qq -y install python3-tk
script:
- python3 -m pytest
......
......@@ -22,11 +22,11 @@ class WBBReadManager(object):
def get_raw_signal(self):
"""Return raw sensor data (TopRight, TopLeft, BottomRight, BottomLeft)."""
top_left = self.mgr.get_channel_samples('tl')
top_right = self.mgr.get_channel_samples('tr')
bottom_right = self.mgr.get_channel_samples('br')
bottom_left = self.mgr.get_channel_samples('bl')
return top_left, top_right, bottom_right, bottom_left
top_left = self.mgr.get_channel_samples('top_left')
top_right = self.mgr.get_channel_samples('top_right')
bottom_right = self.mgr.get_channel_samples('bottom_right')
bottom_left = self.mgr.get_channel_samples('bottom_left')
return top_right, top_left, bottom_right, bottom_left
def get_x(self):
"""Return COPx computed from raw sensor data and adds 'x' channel to ReadManager object."""
......
"""This package provide ReadManager to MNE conversion utilities."""
"""Mixin for readmanager to create MNE.Raw objects."""
import json
from typing import Union
from copy import copy
import numpy
from ..signal.signal_constants import sample_type_from_numpy
from ..signal.read_info_source import MemoryInfoSource
from ..signal.read_data_source import MemoryDataSource
from ..signal.signal_exceptions import NoParameter
from ..signal import read_info_source
from ..signal import read_data_source
from ..tags import read_tags_source
from ..mne_utils.utils import requires_mne, chtype_heuristic
try:
import mne
except ImportError:
pass
def add_stim_chnl(raw):
"""In place add stim channel to mne.Raw."""
if 'STI' not in raw.ch_names:
stim_data = numpy.zeros((1, len(raw.times)))
info = mne.create_info(['OBCI_STIM'], raw.info['sfreq'], ['stim'])
stim_raw = mne.io.RawArray(stim_data, info)
raw.add_channels([stim_raw], force_update_info=True)
def _description_from_tag(tag):
tag_desc = copy(tag)
tag_desc.pop('start_timestamp')
tag_desc.pop('end_timestamp')
return json.dumps(tag_desc)
def tags_from_mne_annotations(ans):
"""Get tags from MNE.Annotations.
:param ans: instance of MNE.Annotations
Returns tags (list of dicts).
"""
tags = []
orig_time = ans.orig_time
if orig_time is None:
orig_time = 0
for onset, duration, desc in zip(ans.onset, ans.duration, ans.description):
tag = json.loads(desc)
tag['start_timestamp'] = onset - orig_time
tag['end_timestamp'] = onset + duration - orig_time
tags.append(tag)
return tags
class ReadManagerMNEMixin:
"""Mixin for ReadManager containing MNE conversions."""
@requires_mne
def get_mne_info(self, channel_types=None):
"""
Create mne.Info.
:param: channel_types optional list of strings with channel types:
‘ecg’, ‘bio’, ‘stim’, ‘eog’, ‘misc’, ‘seeg’, ‘ecog’, ‘mag’, ‘eeg’, ‘ref_meg’, ‘grad’, ‘emg’, ‘hbr’ or ‘hbo’
if None channels types will be selected heuristically.
"""
chnames = self.get_param('channels_names')
samplinf_freq = float(self.get_param('sampling_frequency'))
if channel_types is None:
channel_types = [chtype_heuristic(i) for i in chnames]
try:
info = mne.create_info(ch_names=chnames,
sfreq=samplinf_freq,
ch_types=channel_types,
montage='standard_1005')
except ValueError: # no EEG channels at all
info = mne.create_info(ch_names=chnames,
sfreq=samplinf_freq,
ch_types=channel_types)
self._set_meas_date(info)
return info
@requires_mne
def get_mne_raw(self, channel_types=None):
"""Return MNE.Raw uncut raw signal object.
:param: channel_types optional list of strings with channel types:
‘ecg’, ‘bio’, ‘stim’, ‘eog’, ‘misc’, ‘seeg’, ‘ecog’, ‘mag’, ‘eeg’, ‘ref_meg’, ‘grad’, ‘emg’, ‘hbr’ or ‘hbo’
if None channels types will be selected heuristically
"""
data = self.get_microvolt_samples()
# data is in microvolts, MNE for biosignal data requires volts
data = data * 1e-6
info = self.get_mne_info(channel_types)
dataset = mne.io.RawArray(data, info)
self._set_mne_tags(dataset)
return dataset
@classmethod
@requires_mne
def init_mne(cls,
p_info_source: Union[str, read_info_source.FileInfoSource],
p_data_source: Union[str, read_data_source.FileDataSource],
p_tags_source: Union[str, read_tags_source.FileTagsSource],
**kwargs):
"""Initialize FileInfoSource, FileDataSource and FileTagsSource.
:param: channel_types optional list of strings with channel types:
‘ecg’, ‘bio’, ‘stim’, ‘eog’, ‘misc’, ‘seeg’, ‘ecog’, ‘mag’, ‘eeg’, ‘ref_meg’, ‘grad’, ‘emg’, ‘hbr’ or ‘hbo’
if None channels types will be selected heuristically
"""
return cls(p_info_source, p_data_source, p_tags_source).get_mne_raw(**kwargs)
@classmethod
@requires_mne
def from_mne(cls, mne_raw):
"""Read Raw mne object to ReadManager."""
assert isinstance(mne_raw, mne.io.BaseRaw)
mne_raw.drop_channels(['OBCI_STIM'])
data = mne_raw.get_data() * 1e6 # mne keeps signals in Volts
return cls._rm_from_mne_data(data, mne_raw.info, mne_raw.annotations)
@classmethod
def _rm_from_mne_data(cls, data, info, annotations=None):
mds = MemoryDataSource(data)
mne_stamp_n = info['meas_date']
mne_stamp_microseconds = str(mne_stamp_n[1]).rjust(6, '0').rstrip('0')
if mne_stamp_microseconds == '':
mne_stamp_microseconds = '0'
mne_stamp_s = '{}.{}'.format(mne_stamp_n[0], mne_stamp_microseconds)
chgains = [1.0] * info['nchan']
choffsets = [0] * info['nchan']
mis = MemoryInfoSource(p_params={'sampling_frequency': str(info['sfreq']),
'channels_names': info['ch_names'],
'number_of_channels': str(len(info['ch_names'])),
'first_sample_timestamp': mne_stamp_s,
'channels_gains': chgains,
'channels_offsets': choffsets,
'sample_type': sample_type_from_numpy(data.dtype),
'number_of_samples': str(data.shape[1])
})
read_manager = cls(p_data_source=mds, p_info_source=mis, p_tags_source=None)
if annotations:
read_manager.set_tags(tags_from_mne_annotations(annotations))
return read_manager
def _set_meas_date(self, info):
try:
meas_date_l = (self.get_param('first_sample_timestamp')).split('.')
except NoParameter:
return
try:
meas_date_s = int(meas_date_l[0])
except ValueError:
meas_date_s = 0
try:
meas_date_us = int(meas_date_l[1].lstrip('0'))
except ValueError:
meas_date_us = 0
meas_date = numpy.array([meas_date_s, meas_date_us], dtype=numpy.int32)
info['meas_date'] = meas_date
def mne_annotations(self):
""""Get mne.Annotations from ReadManager tags."""
onsets = []
durations = []
descriptions = []
for tag in self.get_tags():
start_t = tag['start_timestamp']
end_t = tag['end_timestamp']
duration = end_t - start_t
onsets.append(start_t)
durations.append(duration)
descriptions.append(_description_from_tag(tag))
return mne.Annotations(onsets, durations, descriptions)
def mne_events(self):
"""Get mne events from ReadManager tags.
Returns: event array, event ids dictionary (event_id -> tag description).
"""
ans = self.mne_annotations()
unique_descs = numpy.unique(ans.description)
event_id = {}
for desc_id, desc in enumerate(unique_descs):
event_id[desc] = desc_id
sampling_rate = float(self.get_param('sampling_frequency'))
# first column - onset in samples
# second - read only in few special cases, so we can leave it as zeros
# third - event id
events = numpy.zeros((len(ans.onset), 3))
onset_s = numpy.round(ans.onset * sampling_rate)
events[:, 0] = onset_s
for desc_n, desc in enumerate(ans.description):
events[desc_n, 2] = event_id[desc]
return events, event_id
def _set_mne_tags(self, raw):
add_stim_chnl(raw)
ans = self.mne_annotations()
events, _ = self.mne_events()
raw.annotations = ans
raw.add_events(events)
"""Mixin for SmartTagsManager to create MNE.Epochs objects."""
import mne
import numpy
from ..read_manager import ReadManager
from .utils import requires_mne
class SmartTagManagerMNEMixin:
"""Mixin for ReadManager containing MNE conversions."""
@requires_mne
def get_mne_epochs(self, p_tag_type=None, p_from=None, p_len=None, p_func=None, event_descs=None,
channel_types=None):
"""Get MNE.Epochs of given tag type.
You can filter tags by:
:param p_tag_type: string - filters tag name, can be None for any.
:param p_from: float - only use tags after this second of signal, can be None for all.
:param p_len: float - use tags from time: p_from until p_from + p_len, if None from p_from until end of signal.
:p_func: callable, or list of callables func(tag_dict) -> bool - custom tag filtering function.
Takes a tag in dictionary format and returns True if tag meets criteria to be used, False if not.
If list of functions, then returned MNE Object will have epochs with according event_ids set.
Be warned - all parameters in tag descriptions are of type str. Example function:
def func1(tag):
try:
return tag['desc']['value'] == '1'
except:
return False
Tags only will be selected when in their description there is a parameter named "value" with value "1".
:param event_descs: Optional - list of strings - same length as p_func list - event_id descriptions.
:param: channel_types optional list of strings with channel types:
‘ecg’, ‘bio’, ‘stim’, ‘eog’, ‘misc’, ‘seeg’, ‘ecog’, ‘mag’, ‘eeg’, ‘ref_meg’, ‘grad’, ‘emg’, ‘hbr’ or ‘hbo’
if None channels types will be selected heuristically.
"""
if p_func is not None and event_descs is not None:
assert len(p_func) == len(event_descs)
try:
p_func[0]
except TypeError:
p_func = [p_func, ]
if event_descs is None:
event_descs = [str(i) for i in range(len(p_func))]
event_ids_mne = {k: v for k, v in zip(event_descs, range(len(p_func)))}
mne_info = None
tag_def = None
all_epochs = []
events_mne = []
for tagid, func in enumerate(p_func):
stags = self.get_smart_tags(p_tag_type=p_tag_type, p_from=p_from, p_len=p_len,
p_func=func)
for stag in stags:
events_mne.append([1, 1, tagid])
data = stag.get_microvolt_samples()
all_epochs.append(data)
if mne_info is None:
mne_info = stag.get_mne_info(channel_types)
if tag_def is None:
tag_def = stag._tag_def
start_offset = tag_def.start_offset
if len(all_epochs) < 1:
raise Exception('No epochs found meeting p_func criteria')
events_mne_np = numpy.array(events_mne)
# Event timings are not relevant anymore, but must be unique
events_mne_np[:, 0] = numpy.arange(0, len(all_epochs), 1)
# due to numerical instabilities
# data might have one sample more or less
min_length = min(i.shape[1] for i in all_epochs)
all_epochs = [i[:, 0:min_length] for i in all_epochs]
all_epochs_np = numpy.stack(all_epochs) * 1e-6 # to Volts
e_mne = mne.EpochsArray(all_epochs_np,
mne_info,
tmin=start_offset,
events=events_mne_np,
event_id=event_ids_mne)
return e_mne
@classmethod
@requires_mne
def get_smart_tags_from_mne_epochs(cls, epochs):
"""Generate list of small ReadManagers from epochs.
This can be used in the same way as result of SmartTagsManager.get_smart_tags().
"""
tags = []
for e in epochs:
# mne epochs when iterating return data
data = e * 1e6
tags.append(ReadManager._rm_from_mne_data(data, epochs.info, ))
return tags
"""Misc utils needed for obci-MNE conversion."""
try:
import mne
except ImportError:
mne = None
def requires_mne(func):
"""Make sure that mne is loaded, point user to installing."""
def wrapper(*args, **kwargs):
if mne is None:
raise Exception("You have no MNE installed, to install visit http://martinos.org/mne/stable/\n"
"Alternatively you can try running:\n"
"pip3 install --user mne")
return func(*args, **kwargs)
return wrapper
def chtype_heuristic(chname):
"""Channel type heuristic.
Returns channel type string for given channel name.
:param: chname
"""
montage = mne.channels.read_montage('standard_1005')
norm_names = [i.lower() for i in montage.ch_names]
if chname.lower() in norm_names:
return 'eeg'
if 'emg' in chname.lower():
return 'emg'
if 'eog' in chname.lower():
return 'eog'
return 'misc'
......@@ -11,6 +11,7 @@ from typing import Union
import numpy
from .mne_utils.read_manager_mne_mixin import ReadManagerMNEMixin
from . import logging as logger
from .signal import data_raw_write_proxy
from .signal import info_file_proxy
......@@ -22,7 +23,7 @@ from .tags import tags_file_writer
LOGGER = logger.get_logger('read_manager', 'info')
class ReadManager:
class ReadManager(ReadManagerMNEMixin):
"""
A class responsible for reading OpenBCI file format.
......@@ -90,7 +91,8 @@ class ReadManager:
info_writer.set_attributes(params)
# store data
data_writer = data_raw_write_proxy.DataRawWriteProxy(path + '.obci.raw')
p_sample_type = self.get_param('sample_type')
data_writer = data_raw_write_proxy.DataRawWriteProxy(path + '.obci.raw', p_sample_type=p_sample_type)
for sample in self.iter_samples():
data_writer.data_received(sample)
......@@ -98,7 +100,7 @@ class ReadManager:
info_writer.finish_saving()
data_writer.finish_saving()
def get_samples(self, p_from, p_len, p_unit='sample') -> numpy.ndarray:
def get_samples(self, p_from=None, p_len=None, p_unit='sample') -> numpy.ndarray:
"""
Return a two dimensional array of signal values.
......@@ -115,6 +117,21 @@ class ReadManager:
else:
raise Exception('Unrecognised unit type. Should be sample or second!. Abort!')
def get_microvolt_samples(self, *args, **kwargs):
"""
Return a two dimensional array of signal values - adjusted to microvolts.
:param p_from:
:param p_len:
:param p_unit: can be 'sample' or 'second' and makes sense only if p_from and p_len is not ``None``.
:return: numpy array
"""
gains = numpy.array([float(i) for i in self.get_param('channels_gains')])[:, numpy.newaxis]
offsets = numpy.array([float(i) for i in self.get_param('channels_offsets')])[:, numpy.newaxis]
return self.get_samples(*args, **kwargs) * gains + offsets
def get_all_samples(self):
"""Return an array of all samples."""
return self.get_samples(p_from=None, p_len=None, p_unit='sample')
......
......@@ -9,3 +9,12 @@ import numpy
SAMPLE_SIZES = {'DOUBLE': 8, 'FLOAT': 4}
SAMPLE_STRUCT_TYPES = {'DOUBLE': 'd', 'FLOAT': 'f'}
SAMPLE_NUMPY_TYPES = {'DOUBLE': numpy.float64, 'FLOAT': numpy.float32}
def sample_type_from_numpy(dtype):
"""Return DataWriteProxy compatible data type string from numpy dtype."""
sample_type_numpy_d = dict((v, k) for k, v in SAMPLE_NUMPY_TYPES.items())
for k in sample_type_numpy_d.keys():
if dtype == k:
return sample_type_numpy_d[k]
raise KeyError("Unknown numpy type")
......@@ -8,6 +8,7 @@ import queue
from typing import Optional
from functools import cmp_to_key
from .mne_utils.smart_tags_mne_mixin import SmartTagManagerMNEMixin
from . import logging as logger
from . import read_manager
from .signal import signal_exceptions
......@@ -16,7 +17,7 @@ from .tags import smart_tag
LOGGER = logger.get_logger('smart_tags_manager', 'info')
class SmartTagsManager:
class SmartTagsManager(SmartTagManagerMNEMixin):
"""
By now manager gets in init tag definition object and dictionary of files paths.
......
......@@ -9,6 +9,7 @@ test_requirements = [
'pytest-catchlog>=1.2.2',
'flaky>=3.3.0',
'nose>=1.3.7',
'mne==0.14.1'
]
needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv)
pytest_runner_requirement = ['pytest-runner>=2.9']
......@@ -47,4 +48,7 @@ setup(
# 'obci_readmanager = obci_readmanager.cmd.run_readmanager_preset:run',
# ],
# },
)
extras_require={
'test': pytest_runner_requirement + test_requirements,
},
)
......@@ -60,15 +60,15 @@
>>> signal1 = test_signal1()
#numerical stability issues?
#>>> get_percentages_values(signal1, fs, plot=False)
#(50.0, 0.0, 0.0, 50.0)
#the following functions do not seem to be sensible/usable/general, magical values are hardcoded (get_grid method)
>>> get_percentages_values(signal1, fs, plot=False)
(49.375, 0.0, 0.0, 50.0)
#>>> tripping_get_time(signal1, fs)
#(1.0, 1.0)
>>> tripping_get_time(signal1, fs)
(0.98750000000000004, 1.0)
#>>> tripping_get_percentages(signal1, fs, plot=False)
#(50.0, 50.0)
>>> tripping_get_percentages(signal1, fs, plot=False)
(49.375, 50.0)
"""
......
<?xml version="1.0" encoding="utf-8"?><rs:rawSignal xmlns:rs="http://signalml.org/rawsignal"><rs:exportFileName>name</rs:exportFileName><rs:sourceFileName>PILOT_KASIA3_23_06_2010.obci.dat</rs:sourceFileName><rs:sourceFileFormat><rs:rawSignalInfo></rs:rawSignalInfo></rs:sourceFileFormat><rs:samplingFrequency>128</rs:samplingFrequency><rs:channelCount>25</rs:channelCount><rs:sampleCount>2810175</rs:sampleCount><rs:calibration>1.0</rs:calibration><rs:sampleType>DOUBLE</rs:sampleType><rs:byteOrder>LITTLE_ENDIAN</rs:byteOrder><rs:pageSize>20.0</rs:pageSize><rs:blocksPerPage>5</rs:blocksPerPage><rs:channelLabels><rs:label>Fp1</rs:label><rs:label>Fpz</rs:label><rs:label>Fp2</rs:label><rs:label>F7</rs:label><rs:label>F3</rs:label><rs:label>Fz</rs:label><rs:label>F4</rs:label><rs:label>F8</rs:label><rs:label>M1</rs:label><rs:label>C7</rs:label><rs:label>C3</rs:label><rs:label>Cz</rs:label><rs:label>C4</rs:label><rs:label>T8</rs:label><rs:label>M2</rs:label><rs:label>P7</rs:label><rs:label>P3</rs:label><rs:label>Pz</rs:label><rs:label>P4</rs:label><rs:label>P8</rs:label><rs:label>O1</rs:label><rs:label>Oz</rs:label><rs:label>O2</rs:label><rs:label>NIC</rs:label><rs:label>OKO_GORA_DOL</rs:label></rs:channelLabels>
<rs:calibrationGain>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
</rs:calibrationGain>
<rs:calibrationOffset>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
</rs:calibrationOffset>
<rs:exportDate>2010-04-26T11:02:51</rs:exportDate></rs:rawSignal>
<?xml version="1.0" encoding="UTF-8"?>
<rs:rawSignal xmlns:rs="http://signalml.org/rawsignal">
<rs:exportFileName>name</rs:exportFileName>
<rs:sourceFileName>PILOT_KASIA3_23_06_2010.obci.dat</rs:sourceFileName>
<rs:sourceFileFormat>
<rs:rawSignalInfo />
</rs:sourceFileFormat>
<rs:samplingFrequency>128</rs:samplingFrequency>
<rs:channelCount>25</rs:channelCount>
<rs:sampleCount>2810175</rs:sampleCount>
<rs:calibration>1.0</rs:calibration>
<rs:sampleType>DOUBLE</rs:sampleType>
<rs:byteOrder>LITTLE_ENDIAN</rs:byteOrder>
<rs:pageSize>20.0</rs:pageSize>
<rs:blocksPerPage>5</rs:blocksPerPage>
<rs:channelLabels>
<rs:label>Fp1</rs:label>
<rs:label>Fpz</rs:label>
<rs:label>Fp2</rs:label>
<rs:label>F7</rs:label>
<rs:label>F3</rs:label>
<rs:label>Fz</rs:label>
<rs:label>F4</rs:label>
<rs:label>F8</rs:label>
<rs:label>M1</rs:label>
<rs:label>C7</rs:label>
<rs:label>C3</rs:label>
<rs:label>Cz</rs:label>
<rs:label>C4</rs:label>
<rs:label>T8</rs:label>
<rs:label>M2</rs:label>
<rs:label>P7</rs:label>
<rs:label>P3</rs:label>
<rs:label>Pz</rs:label>
<rs:label>P4</rs:label>
<rs:label>P8</rs:label>
<rs:label>O1</rs:label>
<rs:label>Oz</rs:label>
<rs:label>O2</rs:label>
<rs:label>NIC</rs:label>
<rs:label>OKO_GORA_DOL</rs:label>
</rs:channelLabels>
<rs:calibrationGain>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
<rs:calibrationParam>1.0</rs:calibrationParam>
</rs:calibrationGain>
<rs:calibrationOffset>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
<rs:calibrationParam>0.0</rs:calibrationParam>
</rs:calibrationOffset>
<rs:exportDate>2010-04-26T11:02:51</rs:exportDate>
</rs:rawSignal>
\ No newline at end of file
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<rs:rawSignal xmlns:rs="http://signalml.org/rawsignal">
<rs:exportFileName>name</rs:exportFileName>
<rs:sourceFileName>PILOT_KASIA3_23_06_2010.obci.dat</rs:sourceFileName>
<rs:sourceFileFormat>
<rs:rawSignalInfo />
</rs:sourceFileFormat>