| 
					
				 | 
			
			
				@@ -0,0 +1,234 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# greaseweazle/image/kryoflux.py 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Written & released by Keir Fraser <keir.xen@gmail.com> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# This is free and unencumbered software released into the public domain. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# See the file COPYING for more details, or visit <http://unlicense.org>. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import struct, re, math, os 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import itertools as it 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from greaseweazle import error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from greaseweazle.flux import Flux 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from .image import Image 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+mck = 18432000 * 73 / 14 / 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+sck = mck / 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Op: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Nop1  =  8 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Nop2  =  9 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Nop3  = 10 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Ovl16 = 11 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Flux3 = 12 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    OOB   = 13 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class OOB: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    StreamInfo =  1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Index      =  2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    StreamEnd  =  3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    KFInfo     =  4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    EOF        = 13 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class KryoFlux(Image): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, name): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if os.path.isdir(name): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.basename = os.path.join(name, '') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            m = re.search("(\d{2}.[01])?.raw$", name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.basename = name[:m.start()] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def to_file(cls, name, start_cyl, nr_sides): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls = cls(name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls.cyl, cls.side = start_cyl, 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls.nr_sides = nr_sides 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return cls 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def from_file(cls, name): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return cls(name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def get_track(self, cyl, side): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name = self.basename + '%02d.%d.raw' % (cyl, side) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            with open(name, 'rb') as f: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dat = f.read() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except FileNotFoundError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Parse the index-pulse stream positions. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        index = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        idx = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while idx < len(dat): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            op = dat[idx] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if op == Op.OOB: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                oob_op, oob_sz = struct.unpack('<BH', dat[idx+1:idx+4]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if oob_op == OOB.Index: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    pos, = struct.unpack('<I', dat[idx:idx+4]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    index.append(pos) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                elif oob_op == OOB.EOF: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += oob_sz 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif op == Op.Nop3 or op == Op.Flux3: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif op <= 7 or op == Op.Nop2: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Build the flux and index lists for the Flux object. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        flux, flux_list, index_list = [], [], [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        val, index_idx, stream_idx, idx = 0, 0, 0, 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while idx < len(dat): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if index_idx < len(index) and stream_idx >= index[index_idx]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # We've passed an index marker. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                index_list.append(sum(flux)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flux_list += flux 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flux = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                index_idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            op = dat[idx] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if op <= 7: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # Flux2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val += (op << 8) + dat[idx+1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flux.append(val) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif op <= 10: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # Nop1, Nop2, Nop3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nr = op-7 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += nr 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += nr 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif op == Op.Ovl16: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # Ovl16 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val += 0x10000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif op == Op.Flux3: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # Flux3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val += (dat[idx+1] << 8) + dat[idx+2] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flux.append(val) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif op == Op.OOB: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # OOB 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                oob_op, oob_sz = struct.unpack('<BH', dat[idx+1:idx+4]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if oob_op == OOB.StreamInfo or oob_op == OOB.StreamEnd: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    pos, = struct.unpack('<I', dat[idx:idx+4]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    error.check(pos == stream_idx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                "Out-of-sync during KryoFlux stream read") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                elif oob_op == OOB.EOF: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += oob_sz 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # Flux1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val += op 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flux.append(val) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                val = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        flux_list += flux 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Crop partial first revolution. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if len(index_list) > 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            short_index, index_list = index_list[0], index_list[1:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            flux = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for i in range(len(flux_list)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if flux >= short_index: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                flux += flux_list[i] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            flux_list = flux_list[i:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return Flux(index_list, flux_list, sck) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def append_track(self, track): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Converts @track into a KryoFlux stream file.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Check if we should insert an OOB record for the next index mark. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        def check_index(prev_flux): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nonlocal index_idx, dat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if index_idx < len(index) and total >= index[index_idx]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dat += struct.pack('<2BH3I', Op.OOB, OOB.Index, 12, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   stream_idx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   round(index[index_idx] - total + prev_flux), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   round(index[index_idx]/8)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                index_idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Emit a resampled flux value to the KryoFlux data stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        def emit(f): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nonlocal stream_idx, dat, total 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while f >= 0x10000: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dat.append(Op.Ovl16) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f -= 0x10000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                total += 0x10000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                check_index(0x10000) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if f >= 0x800: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dat += struct.pack('>BH', Op.Flux3, f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif Op.OOB < f < 0x100: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dat.append(f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                stream_idx += 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                dat += struct.pack('>H', f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            total += f 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            check_index(f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        flux = track.flux() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        factor = sck / flux.sample_freq 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Start the data stream with a dummy index because our Flux objects 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # are cued to index. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dat = bytearray() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dat += struct.pack('<2BH3I', Op.OOB, OOB.Index, 12, 0, 0, 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Prefix-sum list of resampled index timings. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        index = list(it.accumulate(map(lambda x: x*factor, flux.index_list))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        index_idx = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        stream_idx, total, rem = 0, 0, 0.0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for x in flux.list: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            y = x * factor + rem 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            f = round(y) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            rem = y - f 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            emit(f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # We may not have enough flux to get to the final index value. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Generate a dummy flux just enough to get us there. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if index_idx < len(index): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            emit(math.ceil(index[index_idx] - total) + 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Emit StreamEnd and EOF blocks to terminate the stream. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dat += struct.pack('<2BH2I', Op.OOB, OOB.StreamEnd, 8, stream_idx, 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dat += struct.pack('<2BH', Op.OOB, OOB.EOF, 0x0d0d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        name = self.basename + '%02d.%d.raw' % (self.cyl, self.side) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        with open(name, 'wb') as f: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f.write(dat) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.side += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self.side >= self.nr_sides: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.side = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.cyl += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __enter__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __exit__(self, type, value, tb): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Local variables: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# python-indent: 4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# End: 
			 |