read.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. from greaseweazle.tools import util
  12. from greaseweazle import error
  13. from greaseweazle import usb as USB
  14. from greaseweazle.flux import Flux
  15. def open_image(args):
  16. image_class = util.get_image_class(args.file)
  17. error.check(hasattr(image_class, 'to_file'),
  18. "%s: Cannot create %s image files"
  19. % (args.file, image_class.__name__))
  20. image = image_class.to_file(args.scyl, args.nr_sides)
  21. if args.rate is not None:
  22. image.bitrate = args.rate
  23. for opt, val in args.file_opts.items():
  24. error.check(hasattr(image, 'opts') and hasattr(image.opts, opt),
  25. "%s: Invalid file option: %s" % (args.file, opt))
  26. setattr(image.opts, opt, val)
  27. return image
  28. def normalise_rpm(flux, rpm):
  29. """Adjust all revolutions in Flux object to have specified rotation speed.
  30. """
  31. index_list, freq = flux.index_list, flux.sample_freq
  32. norm_to_index = 60/rpm * flux.sample_freq
  33. norm_flux = []
  34. to_index, index_list = index_list[0], index_list[1:]
  35. factor = norm_to_index / to_index
  36. for x in flux.list:
  37. to_index -= x
  38. if to_index >= 0:
  39. norm_flux.append(x*factor)
  40. continue
  41. if not index_list:
  42. break
  43. n_to_index, index_list = index_list[0], index_list[1:]
  44. n_factor = norm_to_index / n_to_index
  45. norm_flux.append((x+to_index)*factor - to_index*n_factor)
  46. to_index, factor = n_to_index, n_factor
  47. return Flux([norm_to_index]*len(flux.index_list), norm_flux, freq)
  48. def read_to_image(usb, args, image):
  49. """Reads a floppy disk and dumps it into a new image file.
  50. """
  51. for cyl in range(args.scyl, args.ecyl+1):
  52. for side in range(0, args.nr_sides):
  53. print("\rReading Track %u.%u..." % (cyl, side), end="")
  54. usb.seek((cyl, cyl*2)[args.double_step], side)
  55. flux = usb.read_track(args.revs)
  56. if args.rpm is not None:
  57. flux = normalise_rpm(flux, args.rpm)
  58. image.append_track(flux)
  59. print()
  60. # Write the image file.
  61. with open(args.file, "wb") as f:
  62. f.write(image.get_image())
  63. def filename_split_opts(filename):
  64. """Splits a filename from its list of options."""
  65. parts = filename.split('::')
  66. name, opts = parts[0], dict()
  67. for x in map(lambda x: x.split(':'), parts[1:]):
  68. for y in x:
  69. try:
  70. opt, val = y.split('=')
  71. except ValueError:
  72. opt, val = y, True
  73. if opt:
  74. opts[opt] = val
  75. return name, opts
  76. def main(argv):
  77. parser = util.ArgumentParser()
  78. parser.add_argument("--drive", type=util.drive_letter, default='A',
  79. help="drive to read (A,B,0,1,2)")
  80. parser.add_argument("--revs", type=int, default=3,
  81. help="number of revolutions to read per track")
  82. parser.add_argument("--scyl", type=int, default=0,
  83. help="first cylinder to read")
  84. parser.add_argument("--ecyl", type=int, default=81,
  85. help="last cylinder to read")
  86. parser.add_argument("--single-sided", action="store_true",
  87. help="single-sided read")
  88. parser.add_argument("--double-step", action="store_true",
  89. help="double-step drive heads")
  90. parser.add_argument("--rate", type=int, nargs="?",
  91. help="data rate (kbit/s)")
  92. parser.add_argument("--rpm", type=int, nargs="?",
  93. help="normalise to RPM")
  94. parser.add_argument("file", help="output filename")
  95. parser.add_argument("device", nargs="?", help="serial device")
  96. parser.description = description
  97. parser.prog += ' ' + argv[1]
  98. args = parser.parse_args(argv[2:])
  99. args.nr_sides = 1 if args.single_sided else 2
  100. args.file, args.file_opts = filename_split_opts(args.file)
  101. try:
  102. usb = util.usb_open(args.device)
  103. image = open_image(args)
  104. util.with_drive_selected(read_to_image, usb, args, image)
  105. except USB.CmdError as error:
  106. print("Command Failed: %s" % error)
  107. if __name__ == "__main__":
  108. main(sys.argv)
  109. # Local variables:
  110. # python-indent: 4
  111. # End: