def read_track(self, nr_revs, nr_retries=5): retry = 0 while True: try: dat = self._read_track(nr_revs) except CmdError as error: # An error occurred. We may retry on transient overflows. if error.code == Ack.FluxOverflow and retry < nr_retries: retry += 1 else: raise error else: # Success! break # Decode the flux list and read the index-times list. flux_list = self._decode_flux(dat) index_list = self._get_index_times(nr_revs+1) # Clip the initial partial revolution. to_index = index_list[0] for i in range(len(flux_list)): to_index -= flux_list[i] if to_index < 0: flux_list[i] = -to_index flux_list = flux_list[i:] break if to_index >= 0: # We ran out of flux. flux_list = [] index_list = index_list[1:] # Success: Return the requested full index-to-index revolutions. return Flux(index_list, flux_list, self.sample_freq)
def get_track(self, cyl, side, writeout=False): if side >= self.nr_sides or cyl < self.start_cyl: return None off = (cyl - self.start_cyl) * self.nr_sides + side if off >= len(self.track_list): return None tdh, dat = self.track_list[off] if not dat: return None tdh = tdh[4:] # Writeout requires only a single revolution if writeout: tdh = tdh[:12] _, nr, _ = struct.unpack("<3I", tdh) dat = dat[:nr * 2] index_list = [] while tdh: ticks, _, _ = struct.unpack("<3I", tdh[:12]) index_list.append(ticks) tdh = tdh[12:] # Decode the SCP flux data into a simple list of flux times. flux_list = [] val = 0 for i in range(0, len(dat), 2): x = dat[i] * 256 + dat[i + 1] if x == 0: val += 65536 continue flux_list.append(val + x) val = 0 return Flux(index_list, flux_list, SCP.sample_freq)
def normalise_rpm(flux, rpm): """Adjust all revolutions in Flux object to have specified rotation speed. """ index_list, freq = flux.index_list, flux.sample_freq norm_to_index = 60 / rpm * flux.sample_freq norm_flux = [] to_index, index_list = index_list[0], index_list[1:] factor = norm_to_index / to_index for x in flux.list: to_index -= x if to_index >= 0: norm_flux.append(x * factor) continue if not index_list: break n_to_index, index_list = index_list[0], index_list[1:] n_factor = norm_to_index / n_to_index norm_flux.append((x + to_index) * factor - to_index * n_factor) to_index, factor = n_to_index, n_factor return Flux([norm_to_index] * len(flux.index_list), norm_flux, freq)
def get_track(self, cyl, side): tracknr = cyl * 2 + side if not tracknr in self.to_track: return None track = self.to_track[tracknr] tdh, dat = track.tdh, track.dat index_list = [] while tdh: ticks, _, _ = struct.unpack("<3I", tdh[:12]) index_list.append(ticks) tdh = tdh[12:] # Decode the SCP flux data into a simple list of flux times. flux_list = [] val = 0 for i in range(0, len(dat), 2): x = dat[i] * 256 + dat[i + 1] if x == 0: val += 65536 continue flux_list.append(val + x) val = 0 flux = Flux(index_list, flux_list, SCP.sample_freq) flux.splice = track.splice if track.splice is not None else 0 return flux
def get_track(self, cyl, side): off = cyl*2 + side if off >= len(self.track_list): return None tdh, dat = self.track_list[off] if dat is None: return None index_list = [] while tdh: ticks, _, _ = struct.unpack("<3I", tdh[:12]) index_list.append(ticks) tdh = tdh[12:] # Decode the SCP flux data into a simple list of flux times. flux_list = [] val = 0 for i in range(0, len(dat), 2): x = dat[i]*256 + dat[i+1] if x == 0: val += 65536 continue flux_list.append(val + x) val = 0 return Flux(index_list, flux_list, SCP.sample_freq)
def read_track(self, revs, ticks=0, nr_retries=5): retry = 0 while True: try: dat = self._read_track(revs, ticks) except CmdError as error: # An error occurred. We may retry on transient overflows. if error.code == Ack.FluxOverflow and retry < nr_retries: retry += 1 else: raise error else: # Success! break # Decode the flux list and read the index-times list. flux_list, index_list = self._decode_flux(dat) # Success: Return the requested full index-to-index revolutions. return Flux(index_list, flux_list, self.sample_freq, index_cued=False)
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 flux(self, for_writeout=False, cue_at_index=True): # 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) # Weak regions need special processing for correct flux representation. for s, n in self.weak: e = s + n assert 0 < s < e < bitlen pattern = bitarray(endian="big") if n < 400 or self.force_random_weak: # Short weak regions are written with no flux transitions. # Actually we insert a flux transition every 32 bitcells, else # we risk triggering Greaseweazle's No Flux Area generator. pattern.frombytes(b"\x80\x00\x00\x00") bits[s:e] = (pattern * (n // 32 + 1))[:n] else: # Long weak regions we present a fuzzy clock bit in an # otherwise normal byte (16 bits MFM). The byte may be # interpreted as # MFM 0001001010100101 = 12A5 = byte 0x43, or # MFM 0001001010010101 = 1295 = byte 0x47 pattern.frombytes(b"\x12\xA5") bits[s:e] = (pattern * (n // 16 + 1))[:n] for i in range(0, n - 10, 16): x, y = bit_ticks[s + i + 10], bit_ticks[s + i + 11] bit_ticks[s + i + 10], bit_ticks[s + i + 11] = x + y * 0.5, y * 0.5 # To prevent corrupting a preceding sync word by effectively # starting the weak region early, we start with a 1 if we just # clocked out a 0. bits[s] = not bits[s - 1] # Similarly modify the last bit of the weak region. bits[e - 1] = not (bits[e - 2] or bits[e]) if cue_at_index or not for_writeout: # Rotate data to start at the index. index = -self.splice % bitlen if index != 0: bits = bits[index:] + bits[:index] bit_ticks = bit_ticks[index:] + bit_ticks[:index] splice_at_index = index < 4 or bitlen - index < 4 else: splice_at_index = False if not for_writeout: # Do not extend the track for reliable writeout to disk. pass elif not cue_at_index: # We write the track wherever it may fall (uncued). # We stretch the track with extra header gap bytes, in case the # drive spins slow and we need more length to create an overlap. # Thus if the drive spins slow, the track gets a longer header. pos = 4 # We stretch by 10 percent, which is way more than enough. rep = bitlen // (10 * 32) bit_ticks = bit_ticks[pos:pos + 32] * rep + bit_ticks[pos:] bits = bits[pos:pos + 32] * rep + bits[pos:] elif splice_at_index: # Splice is at the index (or within a few bitcells of it). # We stretch the track with extra footer gap bytes, 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 = (self.splice - 4) % bitlen # We stretch by 10 percent, which is way more than enough. rep = bitlen // (10 * 32) bit_ticks = bit_ticks[:pos] + bit_ticks[pos - 32:pos] * rep bits = bits[:pos] + bits[pos - 32:pos] * rep 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 if for_writeout and self.precomp is not None: self.precomp.apply(bits, bit_ticks, ticks_to_index / (self.time_per_rev * 1e9)) # 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 and for_writeout: flux_list.append(flux_ticks) # Package up the flux for return. if for_writeout: flux = WriteoutFlux(ticks_to_index, flux_list, ticks_to_index / self.time_per_rev, index_cued=cue_at_index, terminate_at_index=splice_at_index) else: flux = Flux([ticks_to_index] * 2, flux_list * 2, ticks_to_index / self.time_per_rev, index_cued=True) flux.splice = sum(bit_ticks[:self.splice]) return flux
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) # Weak regions need special processing for correct flux representation. for s, n in self.weak: e = s + n assert s + n <= bitlen if n < 2: continue if n < 400: # Short weak regions are written with no flux transitions. bits[s:e] = bitarray([0]) * n else: # Long weak regions we present a fuzzy clock bit in an # otherwise normal byte (16 bits MFM). The byte may be # interpreted as # MFM 0001001010100101 = 12A5 = byte 0x43, or # MFM 0001001010010101 = 1295 = byte 0x47 pattern = bitarray(endian="big") pattern.frombytes(b"\x12\xA5") bits[s:e] = (pattern * (n // 16 + 1))[:n] for i in range(0, n - 10, 16): x, y = bit_ticks[s + i + 10], bit_ticks[s + i + 11] bit_ticks[s + i + 10], bit_ticks[s + i + 11] = x + y * 0.5, y * 0.5 # To prevent corrupting a preceding sync word by effectively # starting the weak region early, we start with a 1 if we just # clocked out a 0. bits[s] = not (bits[-1] if s == 0 else bits[s - 1]) # Similarly modify the last bit of the weak region. bits[e - 1] = not (bits[e - 2] or bits[e % bitlen]) 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, ticks_to_index / self.time_per_rev) flux.terminate_at_index = splice_at_index return flux