diff --git a/.travis.yml b/.travis.yml index 855a6e6..bdf519e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,6 +44,7 @@ before_script: - wget https://ndownloader.figshare.com/files/14828594 -O 20161027_DM1_1nM_pH7_20MHz1.ptu - wget https://ndownloader.figshare.com/files/13675271 -O TestFile_2.ptu - wget https://ndownloader.figshare.com/files/14850533 -O trace_T2_300s_1_coincidence.ptu + - wget https://ndownloader.figshare.com/files/14890535 -O nanodiamant_histo.phu - wget https://github.com/dwaithe/FCS_point_correlator/raw/master/focuspoint/topfluorPE_2_1_1_1.pt3 - wget https://github.com/Photon-HDF5/phconvert/files/1380341/DNA_FRET_0.5nM.pt3.zip - unzip DNA_FRET_0.5nM.pt3.zip diff --git a/appveyor.yml b/appveyor.yml index 9941d91..c9854d5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -48,6 +48,7 @@ before_test: - ps: wget https://ndownloader.figshare.com/files/14828594 -O 20161027_DM1_1nM_pH7_20MHz1.ptu - ps: wget https://ndownloader.figshare.com/files/13675271 -OutFile TestFile_2.ptu - ps: wget https://ndownloader.figshare.com/files/14850533 -O trace_T2_300s_1_coincidence.ptu + - ps: wget https://ndownloader.figshare.com/files/14890535 -O nanodiamant_histo.phu - ps: wget https://github.com/dwaithe/FCS_point_correlator/raw/master/focuspoint/topfluorPE_2_1_1_1.pt3 -OutFile topfluorPE_2_1_1_1.pt3 - ps: wget https://github.com/Photon-HDF5/phconvert/files/1380341/DNA_FRET_0.5nM.pt3.zip -OutFile DNA_FRET_0.5nM.pt3.zip - 7z e DNA_FRET_0.5nM.pt3.zip diff --git a/notebooks/Example reading PicoQuant PHU files.ipynb b/notebooks/Example reading PicoQuant PHU files.ipynb new file mode 100644 index 0000000..681044d --- /dev/null +++ b/notebooks/Example reading PicoQuant PHU files.ipynb @@ -0,0 +1,157 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "filename = 'data/nanodiamant_histo.phu'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import phconvert as phc\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hist, bin_size, meta = phc.pqreader.load_phu(filename)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hist.shape # the array containing all the histograms" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bin_size # one bin size per histogram" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert all(bin_size == bin_size[0]) # all curves must have the same bin size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "meta.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "meta['acquisition_duration'] # in seconds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "n = 600\n", + "ns = 1e9\n", + "time = np.arange(hist.shape[1]) * bin_size[0] # in s\n", + "\n", + "for i in range(hist.shape[0]):\n", + " plt.plot(time * ns, hist[i], label=f'Curve {i}')\n", + "plt.xlabel('Time (ns)');\n", + "plt.legend()\n", + "plt.xlim(-10, 125)\n", + "plt.grid();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "phc.pqreader._ptu_print_tags(meta['tags'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.equal([1,2,3], [1,2,3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.alltrue" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py37-sm", + "language": "python", + "name": "py37-sm" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.2" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/phconvert/pqreader.py b/phconvert/pqreader.py index b5c6cac..a4b0d43 100644 --- a/phconvert/pqreader.py +++ b/phconvert/pqreader.py @@ -163,7 +163,7 @@ def load_phu(filename): A tuple of histograms, histogram resolution, and tags. The latter is an dictionary of tags contained in the file header. Each item in the dictionary has 'idx', 'type', - 'value' amd 'offset' keys. Some tags also have a 'data' key. + 'value' and 'offset' keys. Some tags also have a 'data' key. Use :func:`_ptu_print_tags` to print the tags as an easy-to-read table. """ @@ -583,16 +583,16 @@ def phu_reader(filename): s = f.read() tags, _ = _read_header_tags(s) - # one as to loop over the different curves (histogram) stored in the phu file + # one has to loop over the different curves (histogram) stored in the phu file Ncurves = tags['HistoResult_NumberOfCurves']['value'] - # all Nbins should be equal between the Ncurves but there is as many tags as curves + # all Nbins should be equal between the Ncurves but there are as many tags as curves Nbins = tags['HistResDscr_HistogramBins'][0]['value'] - histograms = np.zeros((Nbins,Ncurves), dtype='uint32') + histograms = np.zeros((Ncurves, Nbins), dtype='uint32') # populate histograms and get some metadata histo_resolution=[] for ind_curve in range(Ncurves): - histograms[:, ind_curve] = np.frombuffer(s, dtype='uint32', + histograms[ind_curve] = np.frombuffer(s, dtype='uint32', count=tags['HistResDscr_HistogramBins'][ind_curve]['value'], offset=tags['HistResDscr_DataOffset'][ind_curve]['value']) histo_resolution.append( diff --git a/phconvert/test_pqreader.py b/phconvert/test_pqreader.py index b81a1c8..2aac9fe 100644 --- a/phconvert/test_pqreader.py +++ b/phconvert/test_pqreader.py @@ -131,3 +131,36 @@ def test_load_pt3(): acq_duration2 = (timestamps[-1] - timestamps[0]) * meta['timestamps_unit'] # The two acquisition times should match. TODO: find out why they don't #assert abs(acq_duration - acq_duration2) < 0.1 ## BROKEN TEST! + + +def test_load_phu(): + """Test loading PT3 files.""" + fn = 'nanodiamant_histo.phu' + filename = DATADIR + fn + assert os.path.isfile(filename), 'File not found: %s' % filename + hist, bin_size, meta = phc.pqreader.load_phu(filename) + + # test acquisition duration + acq_duration = meta['tags']['MeasDesc_AcquisitionTime']['value'] * 1e-3 + acq_duration2 = meta['acquisition_duration'] + assert acq_duration == acq_duration2 == 10 + + # test hist.shape + tags = meta['tags'] + num_curves = tags['HistoResult_NumberOfCurves']['value'] + num_bins = [tag['value'] for tag in tags['HistResDscr_HistogramBins']] + assert hist.shape[1] == num_bins[0] + assert hist.shape[0] == len(bin_size) == num_curves + + # all value in `num_bins` and in `bin_size` must be the same + def all_values_equal(x): + return all(np.array(x) == x[0]) + assert all_values_equal(num_bins) + assert all_values_equal(bin_size) + + # test `bin_size` + bin_size2 = [tag['value'] for tag in tags['HistResDscr_MDescResolution']] + assert np.array_equal(bin_size, bin_size2) + + +