You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
5.4KB

  1. #!/usr/bin/python2.5
  2. #
  3. # Copyright 2013 Olivier Gillet.
  4. #
  5. # Author: Olivier Gillet (ol.gillet@gmail.com)
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. # -----------------------------------------------------------------------------
  19. #
  20. # Fsk encoder for converting firmware .bin files into .
  21. import numpy
  22. import optparse
  23. import zlib
  24. from avr_audio_bootloader import audio_stream_writer
  25. class FskEncoder(object):
  26. def __init__(
  27. self,
  28. sample_rate=48000,
  29. pause_period=32,
  30. one_period=8,
  31. zero_period=4,
  32. packet_size=256):
  33. self._sr = sample_rate
  34. self._pause_period = pause_period
  35. self._one_period = one_period
  36. self._zero_period = zero_period
  37. self._packet_size = packet_size
  38. self._state = 1
  39. def _encode(self, symbol_stream):
  40. symbol_stream = numpy.array(symbol_stream)
  41. counts = [numpy.sum(symbol_stream == symbol) for symbol in range(3)]
  42. durations = [self._zero_period, self._one_period, self._pause_period]
  43. total_length = numpy.dot(durations, counts)
  44. signal = numpy.zeros((total_length, 1))
  45. state = self._state
  46. index = 0
  47. for symbol in symbol_stream:
  48. d = durations[symbol]
  49. signal[index:index + d] = state
  50. state = -state
  51. index += d
  52. assert index == signal.shape[0]
  53. self._state = state
  54. return signal
  55. def _code_blank(self, duration):
  56. num_symbols = int(duration * self._sr / self._pause_period) + 1
  57. return self._encode([2] * num_symbols)
  58. def _code_packet(self, data):
  59. assert len(data) <= self._packet_size
  60. if len(data) != self._packet_size:
  61. data = data + '\x00' * (self._packet_size - len(data))
  62. crc = zlib.crc32(data) & 0xffffffff
  63. data = map(ord, data)
  64. crc_bytes = [crc >> 24, (crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff]
  65. bytes = [0x55] * 4 + data + crc_bytes
  66. symbol_stream = []
  67. for byte in bytes:
  68. mask = 0x80
  69. for _ in range(0, 8):
  70. symbol_stream.append(1 if (byte & mask) else 0)
  71. mask >>= 1
  72. return self._encode(symbol_stream)
  73. def code(self, data, page_size=1024, blank_duration=0.06):
  74. yield numpy.zeros((1.0 * self._sr, 1)).ravel()
  75. yield self._code_blank(1.0)
  76. if len(data) % page_size != 0:
  77. tail = page_size - (len(data) % page_size)
  78. data += '\xff' * tail
  79. offset = 0
  80. remaining_bytes = len(data)
  81. num_packets_written = 0
  82. while remaining_bytes:
  83. size = min(remaining_bytes, self._packet_size)
  84. yield self._code_packet(data[offset:offset+size])
  85. num_packets_written += 1
  86. if num_packets_written == page_size / self._packet_size:
  87. yield self._code_blank(blank_duration)
  88. num_packets_written = 0
  89. remaining_bytes -= size
  90. offset += size
  91. yield self._code_blank(1.0)
  92. def main():
  93. parser = optparse.OptionParser()
  94. parser.add_option(
  95. '-s',
  96. '--sample_rate',
  97. dest='sample_rate',
  98. type='int',
  99. default=40000,
  100. help='Sample rate in Hz')
  101. parser.add_option(
  102. '-b',
  103. '--pause_period',
  104. dest='pause_period',
  105. type='int',
  106. default=64,
  107. help='Period (in samples) of a blank symbol')
  108. parser.add_option(
  109. '-n',
  110. '--one_period',
  111. dest='one_period',
  112. type='int',
  113. default=16,
  114. help='Period (in samples) of a one symbol')
  115. parser.add_option(
  116. '-z',
  117. '--zero_period',
  118. dest='zero_period',
  119. type='int',
  120. default=8,
  121. help='Period (in samples) of a zero symbol')
  122. parser.add_option(
  123. '-p',
  124. '--packet_size',
  125. dest='packet_size',
  126. type='int',
  127. default=256,
  128. help='Packet size in bytes')
  129. parser.add_option(
  130. '-g',
  131. '--page_size',
  132. dest='page_size',
  133. type='int',
  134. default=64,
  135. help='Flash page size')
  136. parser.add_option(
  137. '-k',
  138. '--blank_duration',
  139. dest='blank_duration',
  140. type='int',
  141. default=8,
  142. help='Duration of the blank between pages, in ms')
  143. parser.add_option(
  144. '-o',
  145. '--output_file',
  146. dest='output_file',
  147. default=None,
  148. help='Write output file to FILE',
  149. metavar='FILE')
  150. options, args = parser.parse_args()
  151. data = file(args[0], 'rb').read()
  152. if len(args) != 1:
  153. logging.fatal('Specify one, and only one firmware .bin file!')
  154. sys.exit(1)
  155. output_file = options.output_file
  156. if not output_file:
  157. if '.bin' in args[0]:
  158. output_file = args[0].replace('.bin', '.wav')
  159. else:
  160. output_file = args[0] + '.wav'
  161. encoder = FskEncoder(
  162. options.sample_rate,
  163. options.pause_period,
  164. options.one_period,
  165. options.zero_period,
  166. options.packet_size)
  167. writer = audio_stream_writer.AudioStreamWriter(
  168. output_file,
  169. options.sample_rate,
  170. 16,
  171. 1)
  172. blank_duration = options.blank_duration * 0.001
  173. for block in encoder.code(data, options.page_size, blank_duration):
  174. if len(block):
  175. writer.append(block)
  176. if __name__ == '__main__':
  177. main()