Skip to content

Commit

Permalink
isolated CCP4 byteorder detection
Browse files Browse the repository at this point in the history
- should help with debugging #50
- added test that checks that the test ccp4 file byteorder is properly detected
  • Loading branch information
orbeckst committed Aug 15, 2018
1 parent 4f1ec45 commit dd41a6e
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 11 deletions.
32 changes: 22 additions & 10 deletions gridData/CCP4.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,14 @@ def __init__(self, filename=None):

def read(self, filename):
"""Populate the instance from the ccp4 file *filename*."""
from struct import calcsize, unpack
if filename is not None:
self.filename = filename
with open(self.filename, 'rb') as ccp4:
h = self.header = self._read_header(ccp4)
nentries = h['nc'] * h['nr'] * h['ns']
# Quick and dirty... slurp it all in one go.
datafmt = h['bsaflag'] + str(nentries) + self._data_bintype
a = np.array(unpack(datafmt, ccp4.read(calcsize(datafmt))))
a = np.array(struct.unpack(datafmt, ccp4.read(struct.calcsize(datafmt))))
self.header['filename'] = self.filename
# TODO: Account for the possibility that y-axis is fastest or
# slowest index, which unfortunately is possible in CCP4.
Expand Down Expand Up @@ -236,13 +235,20 @@ def _delta(self):
delta = lengths / self.shape
return np.diag(delta)

def _read_header(self, ccp4file):
"""Read header bytes, try all possibilities for byte
order/size/alignment."""
# Try all endinaness and alignment options until we find
# something that looks sensible. The machst field could be
# used to obtain endianness, but it does not specify
# alignment.
@staticmethod
def _detect_byteorder(ccp4file):
"""Detect the byteorder of stream `ccp4file` and return format character.
Try all endinaness and alignment options until we find
something that looks sensible ("MAPS " in the first 4 bytes).
(The ``machst`` field could be used to obtain endianness, but
it does not specify alignment.)
.. SeeAlso::
:mod:`struct`
"""
bsaflag = None
ccp4file.seek(52 * 4)
mapbin = ccp4file.read(4)
Expand All @@ -251,10 +257,16 @@ def _read_header(self, ccp4file):
if mapstr.upper() == 'MAP ':
bsaflag = flag
break # Only possible value according to spec.
if bsaflag is None:
else:
raise TypeError(
"Cannot decode header --- corrupted or wrong format?")
ccp4file.seek(0)
return bsaflag

def _read_header(self, ccp4file):
"""Read header bytes"""

bsaflag = self._detect_byteorder(ccp4file)

# Parse the top of the header (4-byte words, 1 to 25).
nheader = struct.calcsize(self._headerfmt)
Expand Down
7 changes: 6 additions & 1 deletion gridData/tests/test_ccp4.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from numpy.testing import (assert_almost_equal,
assert_equal)

from gridData import Grid
from gridData import Grid, CCP4


def test_ccp4():
Expand All @@ -12,3 +12,8 @@ def test_ccp4():
assert_equal(g.grid.size, POINTS)
assert_almost_equal(g.delta, [3./4, .5, 2./3])
assert_equal(g.origin, np.zeros(3))

def test_byteorder():
with open('gridData/tests/test.ccp4', 'rb') as ccp4file:
flag = CCP4.CCP4._detect_byteorder(ccp4file)
assert flag in ("@", "=", "<"), "flag {} is not '<'".format(flag)

0 comments on commit dd41a6e

Please sign in to comment.