Эх сурвалжийг харах

write: Create a new perfect-track container (MasterTrack).
Use this to represent IPF track data.
Intelligently convert the data for write out, depending on the
location of the write splice (overlap).

Keir Fraser 4 жил өмнө
parent
commit
279ca93d47

+ 5 - 0
scripts/greaseweazle/flux.py

@@ -11,6 +11,7 @@ class Flux:
         self.index_list = index_list
         self.list = flux_list
         self.sample_freq = sample_freq
+        self.terminate_at_index = True
 
 
     def __str__(self):
@@ -23,6 +24,10 @@ class Flux:
         return s[:-1]
 
 
+    def flux_for_writeout(self):
+        return self
+
+
     @classmethod
     def from_bitarray(cls, bitarray, bitrate, timing=None):
         if not timing:

+ 8 - 12
scripts/greaseweazle/image/ipf.py

@@ -9,7 +9,7 @@ import os, sys
 import platform
 import ctypes as ct
 from bitarray import bitarray
-from greaseweazle.flux import Flux
+from greaseweazle.track import MasterTrack
 from greaseweazle import error
 
 class CapsDateTimeExt(ct.Structure):
@@ -191,10 +191,6 @@ class IPF:
 
         error.check(ti.weakcnt == 0, "Can't yet handle weak data")
 
-        # We don't really have access to the bitrate. It depends on RPM.
-        # So we assume a rotation rate of 300 RPM (5 rev/sec).
-        bitrate = ti.tracklen * 5
-
         timebuf = None
         if ti.timebuf:
             carray_type = ct.c_uint * ti.timelen
@@ -211,13 +207,13 @@ class IPF:
             # Clip the timing info, if necessary.
             timebuf = timebuf[:ti.tracklen]
 
-        # TODO: Place overlap (write splice) at the correct position.
-        if ti.overlap != 0:
-            trackbuf = trackbuf[ti.overlap:] + trackbuf[:ti.overlap]
-            if timebuf:
-                timebuf = timebuf[ti.overlap:] + timebuf[:ti.overlap]
-
-        return Flux.from_bitarray(trackbuf, bitrate, timebuf)
+        # We don't really have access to the bitrate. It depends on RPM.
+        # So we assume a rotation rate of 300 RPM (5 rev/sec).
+        rpm = 300
+                
+        track = MasterTrack(trackbuf, 60/rpm)
+        track.bit_ticks, track.splice = timebuf, ti.overlap
+        return track
 
 
 # Open and initialise the CAPS library.

+ 5 - 3
scripts/greaseweazle/tools/write.py

@@ -40,10 +40,12 @@ def write_from_image(usb, args, image):
             print("\rWriting Track %u.%u..." % (cyl, side), end="")
             usb.seek(cyl, side)
 
-            flux = image.get_track(cyl, side, writeout=True)
-            if not flux:
+            track = image.get_track(cyl, side, writeout=True)
+            if not track:
                 usb.erase_track(drive_ticks * 1.1)
                 continue
+
+            flux = track.flux_for_writeout()
             
             # @factor adjusts flux times for speed variations between the
             # read-in and write-out drives.
@@ -59,7 +61,7 @@ def write_from_image(usb, args, image):
                 flux_list.append(val)
 
             # Encode the flux times for Greaseweazle, and write them out.
-            usb.write_track(flux_list)
+            usb.write_track(flux_list, flux.terminate_at_index)
 
     print()
 

+ 104 - 0
scripts/greaseweazle/track.py

@@ -0,0 +1,104 @@
+# greaseweazle/track.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 binascii
+from bitarray import bitarray
+from greaseweazle.flux import Flux
+
+# A pristine representation of a track, from a codec and/or a perfect image.
+class MasterTrack:
+
+    @property
+    def bitrate(self):
+        return len(self.bitarray) / self.time_per_rev
+
+    # bits: Track bitcell data (bitarray)
+    # time_per_rev: Time per revolution, in seconds (float)
+    # bit_ticks: Per-bitcell time values, in unitless 'ticks'
+    # splice: Location of the track splice, in bitcells, after the index.
+    def __init__(self, bits, time_per_rev):
+        self.bits = bits
+        self.time_per_rev = time_per_rev
+        self.bit_ticks = None
+        self.splice = 0
+
+    def __str__(self):
+        s = "\nMaster Track:\n"
+        s += (" %d bits, %.1f kbit/s"
+              % (len(self.bits), self.bitrate))
+        if self.bit_ticks:
+            s += " (variable)"
+        s += ("\n Total Time = %.1f ms (%.1f rpm)\n "
+              % (self.time_per_rev * 1000, 60 / self.time_per_rev))
+        #s += str(binascii.hexlify(self.bits.tobytes()))
+        return s
+
+    def flux_for_writeout(self):
+
+        # We're going to mess with the track data, so take a copy.
+        bits = self.bits.copy()
+        bitlen = len(bits)
+
+        # Also copy the bit_ticks array (or create a dummy one), and remember
+        # the total ticks that it contains.
+        bit_ticks = self.bit_ticks.copy() if self.bit_ticks else [1] * bitlen
+        ticks_to_index = sum(bit_ticks)
+
+        splice_at_index = self.splice < 4 or bitlen - self.splice < 4
+
+        if splice_at_index:
+            # Splice is at the index (or within a few bitcells of it).
+            # We stretch the track with extra bytes of filler, in case the
+            # drive motor spins slower than expected and we need more filler
+            # to get us to the index pulse (where the write will terminate).
+            # Thus if the drive spins slow, the track gets a longer footer.
+            pos = bitlen-4 if self.splice < 4 else self.splice-4
+            tick_pattern = bit_ticks[pos-32:pos]
+            fill_pattern = bits[pos-32:pos]
+            # We stretch by 10 percent, which is way more than enough.
+            for i in range(bitlen // (10*32)):
+                bit_ticks[pos:pos+32] = tick_pattern
+                bits[pos:pos+32] = fill_pattern
+                pos += 32
+        else:
+            # Splice is not at the index. We will write more than one
+            # revolution, and terminate the second revolution at the splice.
+            # For the first revolution we repeat the track header *backwards*
+            # to the very start of the write. This is in case the drive motor
+            # spins slower than expected and the write ends before the original
+            # splice position.
+            # Thus if the drive spins slow, the track gets a longer header.
+            bit_ticks += bit_ticks[:self.splice-4]
+            bits += bits[:self.splice-4]
+            pos = self.splice+4
+            fill_pattern = bits[pos:pos+32]
+            while pos >= 32:
+                pos -= 32
+                bits[pos:pos+32] = fill_pattern
+
+        # Convert the stretched track data into flux.
+        bit_ticks_i = iter(bit_ticks)
+        flux_list = []
+        flux_ticks = 0
+        for bit in bits:
+            flux_ticks += next(bit_ticks_i)
+            if bit:
+                flux_list.append(flux_ticks)
+                flux_ticks = 0
+        if flux_ticks:
+            flux_list.append(flux_ticks)
+
+        # Package up the flux for return.
+        flux = Flux([ticks_to_index], flux_list,
+                    self.time_per_rev / ticks_to_index)
+        flux.terminate_at_index = splice_at_index
+        return flux
+
+ 
+# Local variables:
+# python-indent: 4
+# End:

+ 3 - 2
scripts/greaseweazle/usb.py

@@ -360,7 +360,7 @@ class Unit:
 
     ## write_track:
     ## Write the given flux stream to the current track via Greaseweazle.
-    def write_track(self, flux_list, nr_retries=5):
+    def write_track(self, flux_list, terminate_at_index, nr_retries=5):
 
         # Create encoded data stream.
         dat = self._encode_flux(flux_list)
@@ -369,7 +369,8 @@ class Unit:
         while True:
             try:
                 # Write the flux stream to the track via Greaseweazle.
-                self._send_cmd(struct.pack("3B", Cmd.WriteFlux, 3, 1))
+                self._send_cmd(struct.pack("3B", Cmd.WriteFlux, 3,
+                                           int(terminate_at_index)))
                 self.ser.write(dat)
                 self.ser.read(1) # Sync with Greaseweazle
                 self._send_cmd(struct.pack("2B", Cmd.GetFluxStatus, 2))