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.

209 lines
5.9KB

  1. #!/usr/bin/python2.5
  2. #
  3. # Copyright 2013 Olivier Gillet.
  4. #
  5. # Author: Olivier Gillet (ol.gillet@gmail.com)
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy
  8. # of this software and associated documentation files (the "Software"), to deal
  9. # in the Software without restriction, including without limitation the rights
  10. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. # copies of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be included in
  15. # all copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. # THE SOFTWARE.
  24. #
  25. # See http://creativecommons.org/licenses/MIT/ for more information.
  26. #
  27. # -----------------------------------------------------------------------------
  28. #
  29. # Fsk encoder for converting firmware .bin files into .
  30. import numpy
  31. import optparse
  32. import zlib
  33. from stm_audio_bootloader import audio_stream_writer
  34. class FskEncoder(object):
  35. def __init__(
  36. self,
  37. sample_rate=48000,
  38. pause_period=32,
  39. one_period=8,
  40. zero_period=4,
  41. packet_size=256):
  42. self._sr = sample_rate
  43. self._pause_period = pause_period
  44. self._one_period = one_period
  45. self._zero_period = zero_period
  46. self._packet_size = packet_size
  47. self._state = 1
  48. def _encode(self, symbol_stream):
  49. symbol_stream = numpy.array(symbol_stream)
  50. counts = [numpy.sum(symbol_stream == symbol) for symbol in range(3)]
  51. durations = [self._zero_period, self._one_period, self._pause_period]
  52. total_length = numpy.dot(durations, counts)
  53. signal = numpy.zeros((total_length, 1))
  54. state = self._state
  55. index = 0
  56. for symbol in symbol_stream:
  57. d = durations[symbol]
  58. signal[index:index + d] = state
  59. state = -state
  60. index += d
  61. assert index == signal.shape[0]
  62. self._state = state
  63. return signal
  64. def _code_blank(self, duration):
  65. num_symbols = int(duration * self._sr / self._pause_period) + 1
  66. return self._encode([2] * num_symbols)
  67. def _code_packet(self, data):
  68. assert len(data) <= self._packet_size
  69. if len(data) != self._packet_size:
  70. data = data + '\x00' * (self._packet_size - len(data))
  71. crc = zlib.crc32(data) & 0xffffffff
  72. data = map(ord, data)
  73. crc_bytes = [crc >> 24, (crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff]
  74. bytes = [0x55] * 4 + data + crc_bytes
  75. symbol_stream = []
  76. for byte in bytes:
  77. mask = 0x80
  78. for _ in range(0, 8):
  79. symbol_stream.append(1 if (byte & mask) else 0)
  80. mask >>= 1
  81. return self._encode(symbol_stream)
  82. def code(self, data, page_size=1024, blank_duration=0.06):
  83. yield numpy.zeros((1.0 * self._sr, 1)).ravel()
  84. yield self._code_blank(1.0)
  85. if len(data) % page_size != 0:
  86. tail = page_size - (len(data) % page_size)
  87. data += '\xff' * tail
  88. offset = 0
  89. remaining_bytes = len(data)
  90. num_packets_written = 0
  91. while remaining_bytes:
  92. size = min(remaining_bytes, self._packet_size)
  93. yield self._code_packet(data[offset:offset+size])
  94. num_packets_written += 1
  95. if num_packets_written == page_size / self._packet_size:
  96. yield self._code_blank(blank_duration)
  97. num_packets_written = 0
  98. remaining_bytes -= size
  99. offset += size
  100. yield self._code_blank(1.0)
  101. def main():
  102. parser = optparse.OptionParser()
  103. parser.add_option(
  104. '-s',
  105. '--sample_rate',
  106. dest='sample_rate',
  107. type='int',
  108. default=48000,
  109. help='Sample rate in Hz')
  110. parser.add_option(
  111. '-b',
  112. '--pause_period',
  113. dest='pause_period',
  114. type='int',
  115. default=64,
  116. help='Period (in samples) of a blank symbol')
  117. parser.add_option(
  118. '-n',
  119. '--one_period',
  120. dest='one_period',
  121. type='int',
  122. default=16,
  123. help='Period (in samples) of a one symbol')
  124. parser.add_option(
  125. '-z',
  126. '--zero_period',
  127. dest='zero_period',
  128. type='int',
  129. default=8,
  130. help='Period (in samples) of a zero symbol')
  131. parser.add_option(
  132. '-p',
  133. '--packet_size',
  134. dest='packet_size',
  135. type='int',
  136. default=256,
  137. help='Packet size in bytes')
  138. parser.add_option(
  139. '-g',
  140. '--page_size',
  141. dest='page_size',
  142. type='int',
  143. default=1024,
  144. help='Flash page size')
  145. parser.add_option(
  146. '-k',
  147. '--blank_duration',
  148. dest='blank_duration',
  149. type='int',
  150. default=60,
  151. help='Duration of the blank between pages, in ms')
  152. parser.add_option(
  153. '-o',
  154. '--output_file',
  155. dest='output_file',
  156. default=None,
  157. help='Write output file to FILE',
  158. metavar='FILE')
  159. options, args = parser.parse_args()
  160. data = file(args[0], 'rb').read()
  161. if len(args) != 1:
  162. logging.fatal('Specify one, and only one firmware .bin file!')
  163. sys.exit(1)
  164. output_file = options.output_file
  165. if not output_file:
  166. if '.bin' in args[0]:
  167. output_file = args[0].replace('.bin', '.wav')
  168. else:
  169. output_file = args[0] + '.wav'
  170. encoder = FskEncoder(
  171. options.sample_rate,
  172. options.pause_period,
  173. options.one_period,
  174. options.zero_period,
  175. options.packet_size)
  176. writer = audio_stream_writer.AudioStreamWriter(
  177. output_file,
  178. options.sample_rate,
  179. 16,
  180. 1)
  181. blank_duration = options.blank_duration * 0.001
  182. for block in encoder.code(data, options.page_size, blank_duration):
  183. if len(block):
  184. writer.append(block)
  185. if __name__ == '__main__':
  186. main()