| 
							- #!/usr/bin/python2.5
 - #
 - # Copyright 2013 Olivier Gillet.
 - # 
 - # Author: Olivier Gillet (ol.gillet@gmail.com)
 - # 
 - # Permission is hereby granted, free of charge, to any person obtaining a copy
 - # of this software and associated documentation files (the "Software"), to deal
 - # in the Software without restriction, including without limitation the rights
 - # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 - # copies of the Software, and to permit persons to whom the Software is
 - # furnished to do so, subject to the following conditions:
 - # 
 - # The above copyright notice and this permission notice shall be included in
 - # all copies or substantial portions of the Software.
 - # 
 - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 - # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 - # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 - # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 - # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 - # THE SOFTWARE.
 - # 
 - # See http://creativecommons.org/licenses/MIT/ for more information.
 - #
 - # -----------------------------------------------------------------------------
 - #
 - # Qpsk encoder for converting firmware .bin files into .
 - 
 - import numpy
 - import optparse
 - import zlib
 - 
 - from stm_audio_bootloader import audio_stream_writer
 - 
 - 
 - class QpskEncoder(object):
 -   
 -   def __init__(
 -       self,
 -       sample_rate=48000,
 -       carrier_frequency=2000,
 -       bit_rate=8000,
 -       packet_size=256):
 -     period = sample_rate / carrier_frequency
 -     symbol_time = sample_rate / (bit_rate / 2)
 -     
 -     assert (symbol_time % period == 0) or (period % symbol_time == 0)
 -     assert (sample_rate % carrier_frequency) == 0
 -     assert (sample_rate % bit_rate) == 0
 -     
 -     self._sr = sample_rate
 -     self._br = bit_rate
 -     self._carrier_frequency = carrier_frequency
 -     self._sample_index = 0
 -     self._packet_size = packet_size
 -     
 -   @staticmethod
 -   def _upsample(x, factor):
 -     return numpy.tile(x.reshape(len(x), 1), (1, factor)).ravel()
 -     
 -   def _encode_qpsk(self, symbol_stream):
 -     ratio = self._sr / self._br * 2
 -     symbol_stream = numpy.array(symbol_stream)
 -     bitstream_even = 2 * self._upsample(symbol_stream % 2, ratio) - 1
 -     bitstream_odd = 2 * self._upsample(symbol_stream / 2, ratio) - 1
 -     return bitstream_even / numpy.sqrt(2.0), bitstream_odd / numpy.sqrt(2.0)
 -   
 -   def _modulate(self, q_mod, i_mod):
 -     num_samples = len(q_mod)
 -     t = (numpy.arange(0.0, num_samples) + self._sample_index) / self._sr
 -     self._sample_index += num_samples
 -     phase = 2 * numpy.pi * self._carrier_frequency * t
 -     return (q_mod * numpy.sin(phase) + i_mod * numpy.cos(phase))
 - 
 -   def _encode(self, symbol_stream):
 -     return self._modulate(*self._encode_qpsk(symbol_stream))
 -     
 -   def _code_blank(self, duration):
 -     num_zeros = int(duration * self._br / 8) * 4
 -     symbol_stream = numpy.zeros((num_zeros, 1)).ravel().astype(int)
 -     return self._encode(symbol_stream)
 - 
 -   def _code_packet(self, data):
 -     assert len(data) <= self._packet_size
 -     if len(data) != self._packet_size:
 -       data = data + '\x00' * (self._packet_size - len(data))
 - 
 -     crc = zlib.crc32(data) & 0xffffffff
 - 
 -     data = map(ord, data)
 -     # 16x 0 for the PLL ; 8x 21 for the edge detector ; 8x 3030 for syncing
 -     preamble = [0] * 8 + [0x99] * 4 + [0xcc] * 4
 -     crc_bytes = [crc >> 24, (crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff]
 -     bytes = preamble + data + crc_bytes
 -     
 -     symbol_stream = []
 -     for byte in bytes:
 -       symbol_stream.append((byte >> 6) & 0x3)
 -       symbol_stream.append((byte >> 4) & 0x3)
 -       symbol_stream.append((byte >> 2) & 0x3)
 -       symbol_stream.append((byte >> 0) & 0x3)
 -     
 -     return self._encode(symbol_stream)
 -   
 -   def code_intro(self):
 -     yield numpy.zeros((1.0 * self._sr, 1)).ravel()
 -     yield self._code_blank(1.0)
 -   
 -   def code_outro(self, duration=1.0):
 -     yield self._code_blank(duration)
 -   
 -   def code(self, data, page_size=1024, blank_duration=0.06):
 -     if len(data) % page_size != 0:
 -       tail = page_size - (len(data) % page_size)
 -       data += '\xff' * tail
 -     
 -     offset = 0
 -     remaining_bytes = len(data)
 -     num_packets_written = 0
 -     while remaining_bytes:
 -       size = min(remaining_bytes, self._packet_size)
 -       yield self._code_packet(data[offset:offset+size])
 -       num_packets_written += 1
 -       if num_packets_written == page_size / self._packet_size:
 -         yield self._code_blank(blank_duration)
 -         num_packets_written = 0
 -       remaining_bytes -= size
 -       offset += size
 - 
 - STM32F4_SECTOR_BASE_ADDRESS = [
 -   0x08000000,
 -   0x08004000,
 -   0x08008000,
 -   0x0800C000,
 -   0x08010000,
 -   0x08020000,
 -   0x08040000,
 -   0x08060000,
 -   0x08080000,
 -   0x080A0000,
 -   0x080C0000,
 -   0x080E0000
 - ]
 - 
 - STM32F1_PAGE_SIZE = 1024
 - STM32F4_BLOCK_SIZE = 16384
 - STM32F4_APPLICATION_START = 0x08008000
 - 
 - def main():
 -   parser = optparse.OptionParser()
 -   parser.add_option(
 -       '-s',
 -       '--sample_rate',
 -       dest='sample_rate',
 -       type='int',
 -       default=48000,
 -       help='Sample rate in Hz')
 -   parser.add_option(
 -       '-c',
 -       '--carrier_frequency',
 -       dest='carrier_frequency',
 -       type='int',
 -       default=6000,
 -       help='Carrier frequency in Hz')
 -   parser.add_option(
 -       '-b',
 -       '--baud_rate',
 -       dest='baud_rate',
 -       type='int',
 -       default=12000,
 -       help='Baudrate in bps')
 -   parser.add_option(
 -       '-p',
 -       '--packet_size',
 -       dest='packet_size',
 -       type='int',
 -       default=256,
 -       help='Packet size in bytes')
 -   parser.add_option(
 -       '-o',
 -       '--output_file',
 -       dest='output_file',
 -       default=None,
 -       help='Write output file to FILE',
 -       metavar='FILE')
 -   parser.add_option(
 -       '-t',
 -       '--target',
 -       dest='target',
 -       default='stm32f1',
 -       help='Set page size and erase time for TARGET',
 -       metavar='TARGET')
 -       
 -   
 -   options, args = parser.parse_args()
 -   data = file(args[0], 'rb').read()
 -   if len(args) != 1:
 -     logging.fatal('Specify one, and only one firmware .bin file!')
 -     sys.exit(1)
 -   
 -   if options.target not in ['stm32f1', 'stm32f4']:
 -     logging.fatal('Unknown target: %s' % options.target)
 -     sys.exit(2)
 -   
 -   output_file = options.output_file
 -   if not output_file:
 -     if '.bin' in args[0]:
 -       output_file = args[0].replace('.bin', '.wav')
 -     else:
 -       output_file = args[0] + '.wav'
 - 
 -   encoder = QpskEncoder(
 -       options.sample_rate,
 -       options.carrier_frequency,
 -       options.baud_rate,
 -       options.packet_size)
 -   writer = audio_stream_writer.AudioStreamWriter(
 -       output_file,
 -       options.sample_rate,
 -       16,
 -       1)
 -   
 -   # INTRO
 -   for block in encoder.code_intro():
 -     writer.append(block)
 -   
 -   blank_duration = 1.0
 -   if options.target == 'stm32f1':
 -     for block in encoder.code(data, STM32F1_PAGE_SIZE, 0.06):
 -       if len(block):
 -         writer.append(block)
 -   elif options.target == 'stm32f4':
 -     for x in xrange(0, len(data), STM32F4_BLOCK_SIZE):
 -       address = STM32F4_APPLICATION_START + x
 -       block = data[x:x+STM32F4_BLOCK_SIZE]
 -       pause = 2.5 if address in STM32F4_SECTOR_BASE_ADDRESS else 0.2
 -       for block in encoder.code(block, STM32F4_BLOCK_SIZE, pause):
 -         if len(block):
 -           writer.append(block)
 -     blank_duration = 5.0
 -   for block in encoder.code_outro(blank_duration):
 -     writer.append(block)
 - 
 - 
 - if __name__ == '__main__':
 -   main()
 
 
  |