bitcell.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. # greaseweazle/bitcell.py
  2. #
  3. # Written & released by Keir Fraser <keir.xen@gmail.com>
  4. #
  5. # This is free and unencumbered software released into the public domain.
  6. # See the file COPYING for more details, or visit <http://unlicense.org>.
  7. import binascii, itertools
  8. from bitarray import bitarray
  9. class Bitcell:
  10. def __init__(self):
  11. self.clock = 2 / 1000000
  12. self.clock_max_adj = 0.10
  13. self.pll_period_adj = 0.05
  14. self.pll_phase_adj = 0.60
  15. def __str__(self):
  16. s = ""
  17. rev = 0
  18. for b, _ in self.revolution_list:
  19. s += "Revolution %u: " % rev
  20. s += str(binascii.hexlify(b.tobytes())) + "\n"
  21. rev += 1
  22. return s[:-1]
  23. def from_flux(self, flux):
  24. index_list, freq = flux.index_list, flux.sample_freq
  25. clock = self.clock
  26. clock_min = self.clock * (1 - self.clock_max_adj)
  27. clock_max = self.clock * (1 + self.clock_max_adj)
  28. ticks = 0.0
  29. # Per-revolution list of bitcells and bitcell times.
  30. self.revolution_list = []
  31. # Initialise bitcell lists for the first revolution.
  32. bits, times = bitarray(endian='big'), []
  33. to_index = index_list[0] / freq
  34. index_list = index_list[1:]
  35. # Make sure there's enough time in the flux list to cover all
  36. # revolutions by appending a "large enough" final flux value.
  37. for x in itertools.chain(flux.list, [sum(flux.index_list)]):
  38. # Gather enough ticks to generate at least one bitcell.
  39. ticks += x / freq
  40. if ticks < clock/2:
  41. continue
  42. # Clock out zero or more 0s, followed by a 1.
  43. zeros = 0
  44. while True:
  45. # Check if we cross the index mark.
  46. to_index -= clock
  47. if to_index < 0:
  48. self.revolution_list.append((bits, times))
  49. if not index_list:
  50. return
  51. bits, times = bitarray(endian='big'), []
  52. to_index = index_list[0] / freq
  53. index_list = index_list[1:]
  54. ticks -= clock
  55. times.append(clock)
  56. if ticks >= clock/2:
  57. zeros += 1
  58. bits.append(False)
  59. else:
  60. bits.append(True)
  61. break
  62. # PLL: Adjust clock frequency according to phase mismatch.
  63. if zeros <= 3:
  64. # In sync: adjust clock by a fraction of the phase mismatch.
  65. clock += ticks * self.pll_period_adj
  66. else:
  67. # Out of sync: adjust clock towards centre.
  68. clock += (self.clock - clock) * self.pll_period_adj
  69. # Clamp the clock's adjustment range.
  70. clock = min(max(clock, clock_min), clock_max)
  71. # PLL: Adjust clock phase according to mismatch.
  72. new_ticks = ticks * (1 - self.pll_phase_adj)
  73. times[-1] += ticks - new_ticks
  74. ticks = new_ticks
  75. self.revolution_list.append((bits, times))
  76. # Local variables:
  77. # python-indent: 4
  78. # End: