read.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. # greaseweazle/tools/read.py
  2. #
  3. # Greaseweazle control script: Read Disk to Image.
  4. #
  5. # Written & released by Keir Fraser <keir.xen@gmail.com>
  6. #
  7. # This is free and unencumbered software released into the public domain.
  8. # See the file COPYING for more details, or visit <http://unlicense.org>.
  9. description = "Read a disk to the specified image file."
  10. import sys
  11. import importlib
  12. from greaseweazle.tools import util
  13. from greaseweazle import error
  14. from greaseweazle import usb as USB
  15. from greaseweazle.flux import Flux
  16. def open_image(args, image_class):
  17. image = image_class.to_file(args.file)
  18. if args.rate is not None:
  19. image.bitrate = args.rate
  20. for opt, val in args.file_opts.items():
  21. error.check(hasattr(image, 'opts') and hasattr(image.opts, opt),
  22. "%s: Invalid file option: %s" % (args.file, opt))
  23. setattr(image.opts, opt, val)
  24. return image
  25. def normalise_rpm(flux, rpm):
  26. """Adjust all revolutions in Flux object to have specified rotation speed.
  27. """
  28. index_list, freq = flux.index_list, flux.sample_freq
  29. norm_to_index = 60/rpm * flux.sample_freq
  30. norm_flux = []
  31. to_index, index_list = index_list[0], index_list[1:]
  32. factor = norm_to_index / to_index
  33. for x in flux.list:
  34. to_index -= x
  35. if to_index >= 0:
  36. norm_flux.append(x*factor)
  37. continue
  38. if not index_list:
  39. break
  40. n_to_index, index_list = index_list[0], index_list[1:]
  41. n_factor = norm_to_index / n_to_index
  42. norm_flux.append((x+to_index)*factor - to_index*n_factor)
  43. to_index, factor = n_to_index, n_factor
  44. return Flux([norm_to_index]*len(flux.index_list), norm_flux, freq)
  45. def read_and_normalise(usb, args, revs):
  46. flux = usb.read_track(revs)
  47. if args.rpm is not None:
  48. flux = normalise_rpm(flux, args.rpm)
  49. return flux
  50. def read_with_retry(usb, args, cyl, head, decoder):
  51. flux = read_and_normalise(usb, args, args.revs)
  52. if decoder is None:
  53. return flux
  54. dat = decoder(cyl, head, flux)
  55. if dat.nr_missing() != 0:
  56. for retry in range(3):
  57. print("T%u.%u: %s - %d sectors missing - Retrying (%d)"
  58. % (cyl, head, dat.summary_string(),
  59. dat.nr_missing(), retry+1))
  60. flux = read_and_normalise(usb, args, max(args.revs, 3))
  61. dat.decode_raw(flux)
  62. if dat.nr_missing() == 0:
  63. break
  64. return dat
  65. def print_summary(args, summary):
  66. s = 'Cyl-> '
  67. p = -1
  68. for c in args.tracks.cyls:
  69. s += ' ' if c//10==p else str(c//10)
  70. p = c//10
  71. print(s)
  72. s = 'H. S: '
  73. for c in args.tracks.cyls:
  74. s += str(c%10)
  75. print(s)
  76. tot_sec = good_sec = 0
  77. for head in args.tracks.heads:
  78. nsec = max(summary[x].nsec for x in summary if x[1] == head)
  79. for sec in range(nsec):
  80. print("%d.%2d: " % (head, sec), end="")
  81. for cyl in args.tracks.cyls:
  82. s = summary[cyl,head]
  83. if sec > s.nsec:
  84. print(" ", end="")
  85. else:
  86. tot_sec += 1
  87. if s.has_sec(sec): good_sec += 1
  88. print("." if s.has_sec(sec) else "X", end="")
  89. print()
  90. if tot_sec != 0:
  91. print("Found %d sectors of %d (%d%%)" %
  92. (good_sec, tot_sec, good_sec*100/tot_sec))
  93. def read_to_image(usb, args, image, decoder=None):
  94. """Reads a floppy disk and dumps it into a new image file.
  95. """
  96. summary = dict()
  97. for t in args.tracks:
  98. cyl, head = t.cyl, t.head
  99. usb.seek(t.physical_cyl, head)
  100. dat = read_with_retry(usb, args, cyl, head, decoder)
  101. print("T%u.%u: %s" % (cyl, head, dat.summary_string()))
  102. summary[cyl,head] = dat
  103. image.emit_track(cyl, head, dat)
  104. if decoder is not None:
  105. print_summary(args, summary)
  106. def main(argv):
  107. parser = util.ArgumentParser(usage='%(prog)s [options] file')
  108. parser.add_argument("--device", help="greaseweazle device name")
  109. parser.add_argument("--drive", type=util.drive_letter, default='A',
  110. help="drive to read (A,B,0,1,2)")
  111. parser.add_argument("--format", help="disk format")
  112. parser.add_argument("--revs", type=int,
  113. help="number of revolutions to read per track")
  114. parser.add_argument("--tracks", type=util.TrackSet,
  115. help="which tracks to read")
  116. parser.add_argument("--rate", type=int, help="data rate (kbit/s)")
  117. parser.add_argument("--rpm", type=int, help="convert drive speed to RPM")
  118. parser.add_argument("file", help="output filename")
  119. parser.description = description
  120. parser.prog += ' ' + argv[1]
  121. args = parser.parse_args(argv[2:])
  122. args.file, args.file_opts = util.split_opts(args.file)
  123. try:
  124. usb = util.usb_open(args.device)
  125. image_class = util.get_image_class(args.file)
  126. if not args.format and hasattr(image_class, 'default_format'):
  127. args.format = image_class.default_format
  128. decoder, def_tracks = None, None
  129. if args.format:
  130. try:
  131. mod = importlib.import_module('greaseweazle.codec.'
  132. + args.format)
  133. decoder = mod.decode_track
  134. except (ModuleNotFoundError, AttributeError) as ex:
  135. raise error.Fatal("Unknown format '%s'" % args.format) from ex
  136. def_tracks = util.TrackSet(mod.default_trackset)
  137. if args.revs is None: args.revs = mod.default_revs
  138. if def_tracks is None:
  139. def_tracks = util.TrackSet('c=0-81:h=0-1')
  140. if args.revs is None: args.revs = 3
  141. if args.tracks is not None:
  142. def_tracks.update_from_trackspec(args.tracks.trackspec)
  143. args.tracks = def_tracks
  144. print("Reading %s revs=%d" % (args.tracks, args.revs))
  145. with open_image(args, image_class) as image:
  146. util.with_drive_selected(read_to_image, usb, args, image,
  147. decoder=decoder)
  148. except USB.CmdError as err:
  149. print("Command Failed: %s" % err)
  150. if __name__ == "__main__":
  151. main(sys.argv)
  152. # Local variables:
  153. # python-indent: 4
  154. # End: