diff options
author | Sebastian Muszytowski <sebastian@muszytowski.net> | 2016-06-03 15:48:16 +0200 |
---|---|---|
committer | Sebastian Muszytowski <sebastian@muszytowski.net> | 2016-06-03 15:48:16 +0200 |
commit | 7c56be8a724e2b0647e5a6cbd55c7b0eb948206b (patch) | |
tree | 26cd8f60a780b03a04c92503775c08eaa29dd6c2 /utilities | |
parent | 2bf8aede5419574a785ad89ffa0e4c87a9fff71d (diff) |
initial commit of all data
Diffstat (limited to 'utilities')
-rw-r--r-- | utilities/blinkenrocket.py | 315 | ||||
-rw-r--r-- | utilities/flasher.py | 78 | ||||
-rw-r--r-- | utilities/font_to_json.py | 27 | ||||
-rw-r--r-- | utilities/mirror_font.py | 25 | ||||
-rwxr-xr-x | utilities/modem_transmit | 16 | ||||
-rw-r--r-- | utilities/test_blinkenrocket.py | 117 |
6 files changed, 578 insertions, 0 deletions
diff --git a/utilities/blinkenrocket.py b/utilities/blinkenrocket.py new file mode 100644 index 0000000..d16b7cf --- /dev/null +++ b/utilities/blinkenrocket.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python + +import sys, wave + +class modem: + + # Modem specific constants + bits = [[3 * chr(0), 5 * chr(0)], [3 * chr(255), 5 * chr(255)]] + sync = [17 * chr(0), 17 * chr(255)] + # Variable to alternate high and low + hilo = 0 + supportedFrequencies = [16000,22050,24000,32000,44100,48000] + cnt = 0 + # Data variables + data = [] + parity = True + frequency = 48000 + + # Hamming code translation table + _hammingCalculateParityLowNibble = [0, 3, 5, 6, 6, 5, 3, 0, 7, 4, 2, 1, 1, 2, 4, 7] + _hammingCalculateParityHighNibble = [0, 9, 10, 3, 11, 2, 1, 8, 12, 5, 6, 15, 7, 14, 13, 4] + + # Almost nothing here + def __init__(self, data=[], parity=True, frequency=48000): + self.data = data + self.parity = parity + self.frequency = frequency if frequency in self.supportedFrequencies else 48000 + self.cnt = 0 + + # Calculate Hamming parity for 12,8 code (12 bit of which 8bit data) + def hammingCalculateParity128(self, byte): + return self._hammingCalculateParityLowNibble[byte&0x0F] ^ self._hammingCalculateParityHighNibble[byte >> 4] + + # Calculate Hamming parity for 24,16 code (24 bit of which 16 bit are data) + def hammingCalculateParity2416(self, first, second): + return self.hammingCalculateParity128(second) << 4 | self.hammingCalculateParity128(first) + + # Generate one sync-pulse + def syncsignal(self): + self.hilo ^= 1 + return self.sync[self.hilo] + + # Generate a number of sync signals + def generateSyncSignal(self, number): + sound = "" + for i in xrange(number): + sound += self.syncsignal() + return sound + + # Decode bits to modem signals + def modemcode(self, byte): + bleep = "" + for x in xrange(8): + self.hilo ^= 1 + bleep += self.bits[self.hilo][byte & 0x01] + byte >>= 1 + return bleep + + # Return <length> samples of silence + def silence(self, length): + return chr(127) * length + + # Set data for modem code + def setData(self, data): + self.data = data + + # Set whether to use parity or not + def setParity(self, parity): + self.parity = parity + + # Set the frequency for the audio + def setFrequency(self, frequency): + self.frequency = frequency if frequency in self.supportedFrequencies else 48000 + + # Generates the audio frames based on the data + def generateAudioFrames(self): + if self.parity: + tmpdata = [] + # for uneven length data, we have to append a null byte + if not len(self.data) % 2 == 0: + self.data.append(chr(0)) + # insert the parity information every two bytes, sorry for the heavy casting + for index in range(0, len(self.data), 2): + tmpdata.extend(self.data[index:index+2]) + tmpdata.append(chr(self.hammingCalculateParity2416(ord(self.data[index]),ord(self.data[index+1])))) + self.data = tmpdata + # generate the audio itself + # add 1000ms of sync signal before the data + # (some sound cards take a while to produce a proper output signal) + sound = self.generateSyncSignal(3000) + # process the data and insert sync signal every 10 bytes + for byte in self.data: + sound += self.modemcode(ord(byte)) + self.cnt += 1 + if self.cnt == 9: # ! do not send sync inside (byte1 byte2 parity) triples + sound += self.generateSyncSignal(4) + self.cnt = 0 + # add some sync signals in the end + sound += self.generateSyncSignal(4) + return sound + + def saveAudio(self,filename): + wav = wave.open(filename, 'wb') + wav.setparams((1, 1, self.frequency, 0, "NONE", None)) + wav.writeframes(self.generateAudioFrames()) + wav.close() + +class Frame( object ): + """ Returns the frame information """ + def getFrameHeader(self): + raise NotImplementedError("You should implement this!") + + """ Returns the representation """ + def getRepresentation(self): + raise NotImplementedError("Should have implemented this") + +class textFrame(Frame): + text = "" + speed = 0 + delay = 0 + direction = 0 + # identifier as of message specification: 0001 + identifier = 0x01 + + def __init__(self,text,speed=13,delay=0,direction=0): + self.text = text + self.setSpeed(speed) + self.setDelay(delay) + self.setDirection(direction) + + def setSpeed(self,speed): + self.speed = speed if speed < 16 else 1 + + def setDelay(self,delay): + self.delay = delay if delay < 16 else 0 + + def setDirection(self,direction): + self.direction = direction if direction in [0,1] else 0 + + # Frame header: 4 bit type + 12 bit length + def getFrameHeader(self): + return [chr(self.identifier << 4 | len(self.text) >> 8), chr(len(self.text) & 0xFF) ] + + # Header -> 4bit speed, 4 bit delay, 4 bit direction, 4 bit zero + def getHeader(self): + return [chr(self.speed << 4 | self.delay), chr(self.direction << 4 | 0x00)] + + def getRepresentation(self): + retval = [] + retval.extend(self.getFrameHeader()) + retval.extend(self.getHeader()) + retval.extend(list(self.text)) + return retval + +class animationFrame(Frame): + animation = [] + speed = 0 + delay = 0 + # identifier as per specification: 0010 + identifier = 0x02 + + def __init__(self,animation,speed=13,delay=0): + self.setAnimation(animation) + self.setSpeed(speed) + self.setDelay(delay) + + def setAnimation(self,animation): + if len(animation) % 8 is not 0: + raise Exception + else: + self.animation = animation + + def setSpeed(self,speed): + self.speed = speed if speed < 16 else 1 + + def setDelay(self,delay): + self.delay = delay if delay < 16 else 0 + + # Frame header: 4 bit type + 12 bit length + def getFrameHeader(self): + return [chr(self.identifier << 4 | len(self.animation) >> 8), chr(len(self.animation) & 0xFF) ] + + # Header -> 4bit zero, 4bit speed, 4 bit zero, 4 bit direction + def getHeader(self): + return [chr(self.speed), chr(self.delay)] + + def getRepresentation(self): + retval = [] + retval.extend(self.getFrameHeader()) + retval.extend(self.getHeader()) + retval.extend(self.animation) + return retval + + +class blinkenrocket(): + + eeprom_size = 65536 + startcode = chr(0x99) + patterncode = chr(0xA9) + endcode = chr(0x84) + frames = [] + + def __init__(self,eeprom_size=65536): + self.eeprom_size = eeprom_size if eeprom_size < 256*1024*1024 else 65536 + + def addFrame(self, frame): + if not isinstance(frame, Frame): + raise RuntimeError("Incorrect frame supplied") + else: + self.frames.append(frame) + + def getMessage(self): + output = [self.startcode, self.startcode] + for frame in self.frames: + output.extend([self.patterncode,self.patterncode]) + output.extend(frame.getRepresentation()) + output.extend([self.endcode,self.endcode]) + return output + + + + +if __name__ == '__main__': + m = modem(parity=True, frequency=48000) + b = blinkenrocket() + + for message in sys.argv[2:]: + b.addFrame(textFrame(message, speed=13)) + b.addFrame(textFrame(" \x04 ")) + b.addFrame(animationFrame(map(lambda x : chr(x), [0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255]), speed=0)) + b.addFrame(animationFrame(map(lambda x : chr(x), [ + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 16, + 0, 0, 0, 0, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 0, 0, 64, + 0, 0, 0, 0, 0, 0, 0, 128, + 0, 0, 0, 0, 0, 0, 128, 0, + 0, 0, 0, 0, 0, 128, 0, 0, + 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 128, 0, 0, 0, 0, + 0, 0, 128, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 0, 0, 0, + 128, 0, 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 64, 0, 0, + 0, 0, 0, 0, 64, 0, 0, 0, + 0, 0, 0, 64, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, 0, 0, + 0, 64, 0, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 16, 0, 0, 0, 0, 0, 0, + 0, 8, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 8, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, + 0, 0, 0, 32, 0, 0, 0, 0, + 0, 0, 32, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 16, 0, 0, 0, + 0, 0, 0, 16, 0, 0, 0, 0, + 0, 0, 0, 24, 24, 0, 0, 0, + 0, 0, 60, 36, 36, 60, 0, 0, + 0, 126, 66, 66, 66, 66, 126, 0, + 255, 129, 129, 129, 129, 129, 129, 255, + 0, 0, 0, 0, 0, 0, 0, 0, + ]), speed=15, delay=1)) + b.addFrame(animationFrame(map(lambda x : chr(x), [ + 0, 0, 0, 24, 24, 0, 0, 0, + 0, 0, 60, 36, 36, 60, 0, 0, + 0, 126, 66, 66, 66, 66, 126, 0, + 255, 129, 129, 129, 129, 129, 129, 255, + 0, 0, 0, 0, 0, 0, 0, 0, + ]), speed=14, delay=1)) + + m.setData(b.getMessage()) + m.saveAudio(sys.argv[1]) + + #print b.getMessage() + + diff --git a/utilities/flasher.py b/utilities/flasher.py new file mode 100644 index 0000000..b39d913 --- /dev/null +++ b/utilities/flasher.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +import sys +import os +from termcolor import colored + +try: + import tty, termios +except ImportError: + try: + import msvcrt + except ImportError: + raise ImportError('getch not available') + else: + getch = msvcrt.getch +else: + def getch(): + """getch() -> key character + + Read a single keypress from stdin and return the resulting character. + Nothing is echoed to the console. This call will block if a keypress + is not already available, but will not wait for Enter to be pressed. + + If the pressed key was a modifier key, nothing will be detected; if + it were a special function key, it may return the first character of + of an escape sequence, leaving additional characters in the buffer. + """ + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + +code_okay = """ + ####### ## ## ### ## ## +## ## ## ## ## ## ## ## +## ## ## ## ## ## #### +## ## ##### ## ## ## +## ## ## ## ######### ## +## ## ## ## ## ## ## + ####### ## ## ## ## ## +""" + +code_error = """ +######## ######## ######## ####### ######## +## ## ## ## ## ## ## ## ## +## ## ## ## ## ## ## ## ## +###### ######## ######## ## ## ######## +## ## ## ## ## ## ## ## ## +## ## ## ## ## ## ## ## ## +######## ## ## ## ## ####### ## ## +""" + +def printOkay(): + print colored(code_okay, 'green') + +def printError(): + print colored(code_error, 'red') + +flash_command = sys.argv[1] + +print colored("Using the following command to flash: %s" % flash_command, "green") + +while True: + print colored("Press any key to continue or 'q' to quit.","yellow") + char = getch() + print char + if ord(char) is ord('q'): + sys.exit(0) + return_code = os.system(flash_command) + if return_code > 0: + printError() + else: + printOkay() + diff --git a/utilities/font_to_json.py b/utilities/font_to_json.py new file mode 100644 index 0000000..6d1b667 --- /dev/null +++ b/utilities/font_to_json.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +import sys +import os +import re +import json + +result = {} + +with open('font.h') as font: + for line in font: + if 'PROGMEM' in line: + hexes = re.findall(r'(0x[0-9a-fA-F]+)',line) + contents = hexes[1:] + length = len(contents) + literal = int(re.findall(r'chr_([0-9]+)',line)[0]) + description = re.findall(r'\/\/(.*)$',line)[0].strip() + #for row in contents: + # print '{0:08b}'.format(int(row,16)) #.replace("1",u"\u2588").replace("0",u"\u25A2") + result[str(literal)] = { + 'literal' : literal, + 'description' : description, + 'hexcolumns' : contents, + 'length' : length + } + +print json.dumps(result) diff --git a/utilities/mirror_font.py b/utilities/mirror_font.py new file mode 100644 index 0000000..db5b5c3 --- /dev/null +++ b/utilities/mirror_font.py @@ -0,0 +1,25 @@ +#!/usr/bin/python + +import sys +import os +import re +import json + +result = {} + +with open('font.h') as font: + for line in font: + if 'PROGMEM' in line: + hexes = re.findall(r'(0x[0-9a-fA-F]+)',line) + prefix = hexes[:1] + contents = hexes[1:] + length = len(contents) + literal = int(re.findall(r'chr_([0-9]+)',line)[0]) + description = re.findall(r'\/\/(.*)$',line)[0].strip() + newhexes = [] + newhexes.append(prefix[0]) + for row in contents: + bitstring = '{0:08b}'.format(int(row,16)) + newbits = bitstring[::-1] + newhexes.append( format(int(newbits,2), '#04x')) + print "const unsigned char PROGMEM chr_%s[] = {%s}; // %s" % (literal,','.join(newhexes),description) diff --git a/utilities/modem_transmit b/utilities/modem_transmit new file mode 100755 index 0000000..bb18850 --- /dev/null +++ b/utilities/modem_transmit @@ -0,0 +1,16 @@ +#!/bin/sh + +OUT="$(mktemp)" +trap "rm -f '$OUT'" EXIT INT QUIT TERM + +python blinkenrocket.py $OUT "$@" + +case "$(uname -s)" in +Darwin) + play $OUT + ;; +Linux) + aplay $OUT + ;; +esac + diff --git a/utilities/test_blinkenrocket.py b/utilities/test_blinkenrocket.py new file mode 100644 index 0000000..62bd0e3 --- /dev/null +++ b/utilities/test_blinkenrocket.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +import unittest +from blinkenrocket import * + +class TestFrame(unittest.TestCase): + + def test_notImplemented(self): + frame = Frame() + with self.assertRaises(NotImplementedError): + frame.getRepresentation() + +class TestAnimation(unittest.TestCase): + + def test_speedDefault(self): + anim = animationFrame([]) + self.assertEquals(ord(anim.getHeader()[0]),1) + + def test_speedOkay(self): + anim = animationFrame([],speed=7) + self.assertEquals(ord(anim.getHeader()[0]),7) + + def test_speedNotOkay(self): + anim = animationFrame([],speed=70) + self.assertEquals(ord(anim.getHeader()[0]),1) + + def test_delayDefault(self): + anim = animationFrame([]) + self.assertEquals(ord(anim.getHeader()[1]),0) + + def test_delayOkay(self): + anim = animationFrame([],delay=7) + self.assertEquals(ord(anim.getHeader()[1]),7) + + def test_delayNotOkay(self): + anim = animationFrame([],delay=70) + self.assertEquals(ord(anim.getHeader()[1]),0) + + def test_illegalLength(self): + with self.assertRaises(Exception): + anim = animationFrame([0x11,0x12,0x13,0x14]) + + def test_allowedLength(self): + anim = animationFrame([0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18]) + + def test_defaultHeaderOK(self): + anim = animationFrame([]) + self.assertEquals(anim.getHeader(),[chr(1),chr(0)]) + + def test_differentHeaderOK(self): + anim = animationFrame([],speed=7,delay=8) + self.assertEquals(anim.getHeader(),[chr(7),chr(8)]) + +class TestText(unittest.TestCase): + + def test_speedDefault(self): + text = textFrame([]) + self.assertEquals(ord(text.getHeader()[0]),(1 << 4 | 0)) + + def test_speedOkay(self): + text = textFrame([],speed=7) + self.assertEquals(ord(text.getHeader()[0]),(7 << 4 | 0)) + + def test_speedNotOkay(self): + text = textFrame([],speed=70) + self.assertEquals(ord(text.getHeader()[0]),(1 << 4 | 0)) + + def test_delayDefault(self): + text = textFrame([]) + self.assertEquals(ord(text.getHeader()[0]),(1 << 4 | 0)) + + def test_delayOkay(self): + text = textFrame([],delay=7) + self.assertEquals(ord(text.getHeader()[0]),(1 << 4 | 7)) + + def test_delayNotOkay(self): + text = textFrame([],delay=70) + self.assertEquals(ord(text.getHeader()[0]),(1 << 4 | 0)) + + def test_directionDefault(self): + text = textFrame([]) + self.assertEquals(ord(text.getHeader()[1]),0) + + def test_directionOkay(self): + text = textFrame([],direction=1) + self.assertEquals(ord(text.getHeader()[1]),(1 << 4 | 0)) + + def test_directionNotOkay(self): + text = textFrame([],direction=7) + self.assertEquals(ord(text.getHeader()[1]),0) + + def test_defaultHeaderOK(self): + text = textFrame([]) + self.assertEquals(text.getHeader(),[chr(1 << 4 | 0),chr(0)]) + + def test_differentHeaderOK(self): + text = textFrame([],speed=7,delay=8,direction=1) + self.assertEquals(text.getHeader(),[chr(7 << 4 | 8),chr(1 << 4 | 0)]) + +class TestBlinkenrocket(unittest.TestCase): + + def test_addFrameFail(self): + br = blinkenrocket() + with self.assertRaises(Exception): + br.addFrame([]) + + def test_addFrameText(self): + text = textFrame("MUZY",speed=7,delay=8,direction=1) + self.assertEquals(text.getFrameHeader(),[chr(0x01 << 4), chr(4)]) + self.assertEquals(text.getHeader(),[chr(7 << 4 | 8),chr(1 << 4 | 0)]) + self.assertEquals(text.getRepresentation(),[chr(0x01 << 4), chr(4),chr(7 << 4 | 8),chr(1 << 4 | 0),'M','U','Z','Y']) + br = blinkenrocket() + br.addFrame(text) + expect = [chr(0x99),chr(0x99),chr(0xA9),chr(0xA9),chr(0x01 << 4), chr(4),chr(7 << 4 | 8),chr(1 << 4 | 0),'M','U','Z','Y',chr(0x84),chr(0x84)] + self.assertEquals(br.getMessage(),expect) + +if __name__ == '__main__': + unittest.main() |