bitcell.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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
  8. import itertools as it
  9. from bitarray import bitarray
  10. class Bitcell:
  11. def __init__(self, clock = 2e-6, flux = None):
  12. self.clock = clock
  13. self.clock_max_adj = 0.10
  14. self.pll_period_adj = 0.05
  15. self.pll_phase_adj = 0.60
  16. self.bitarray = bitarray(endian='big')
  17. self.timearray = []
  18. self.revolutions = []
  19. if flux is not None:
  20. self.from_flux(flux)
  21. def __str__(self):
  22. s = ""
  23. for rev in range(len(self.revolutions)):
  24. b, _ = self.get_revolution(rev)
  25. s += "Revolution %u: " % rev
  26. s += str(binascii.hexlify(b.tobytes())) + "\n"
  27. return s[:-1]
  28. def get_revolution(self, nr):
  29. start = sum(self.revolutions[:nr])
  30. end = start + self.revolutions[nr]
  31. return self.bitarray[start:end], self.timearray[start:end]
  32. def from_flux(self, flux):
  33. freq = flux.sample_freq
  34. clock = self.clock
  35. clock_min = self.clock * (1 - self.clock_max_adj)
  36. clock_max = self.clock * (1 + self.clock_max_adj)
  37. ticks = 0.0
  38. index_iter = iter(map(lambda x: x/freq, flux.index_list))
  39. bits, times = bitarray(endian='big'), []
  40. to_index = next(index_iter)
  41. # Make sure there's enough time in the flux list to cover all
  42. # revolutions by appending a "large enough" final flux value.
  43. for x in it.chain(flux.list, [sum(flux.index_list)]):
  44. # Gather enough ticks to generate at least one bitcell.
  45. ticks += x / freq
  46. if ticks < clock/2:
  47. continue
  48. # Clock out zero or more 0s, followed by a 1.
  49. zeros = 0
  50. while True:
  51. # Check if we cross the index mark.
  52. to_index -= clock
  53. if to_index < 0:
  54. self.bitarray += bits
  55. self.timearray += times
  56. self.revolutions.append(len(times))
  57. assert len(times) == len(bits)
  58. try:
  59. to_index += next(index_iter)
  60. except StopIteration:
  61. return
  62. bits, times = bitarray(endian='big'), []
  63. ticks -= clock
  64. times.append(clock)
  65. if ticks >= clock/2:
  66. zeros += 1
  67. bits.append(False)
  68. else:
  69. bits.append(True)
  70. break
  71. # PLL: Adjust clock frequency according to phase mismatch.
  72. if zeros <= 3:
  73. # In sync: adjust clock by a fraction of the phase mismatch.
  74. clock += ticks * self.pll_period_adj
  75. else:
  76. # Out of sync: adjust clock towards centre.
  77. clock += (self.clock - clock) * self.pll_period_adj
  78. # Clamp the clock's adjustment range.
  79. clock = min(max(clock, clock_min), clock_max)
  80. # PLL: Adjust clock phase according to mismatch.
  81. new_ticks = ticks * (1 - self.pll_phase_adj)
  82. times[-1] += ticks - new_ticks
  83. ticks = new_ticks
  84. # We can't get here: We should run out of indexes before we run
  85. # out of flux.
  86. assert False
  87. # Local variables:
  88. # python-indent: 4
  89. # End: