diff --git a/client_sw/matrixSender.py b/client_sw/matrixSender.py index 8ad4689..d355440 100644 --- a/client_sw/matrixSender.py +++ b/client_sw/matrixSender.py @@ -18,7 +18,6 @@ class MatrixSender(object): global threadrunning threadrunning=False - def __init__(self, udphost, udpport, img_size=(160,48), bitsperpixel=1): self._udphost = udphost @@ -31,58 +30,21 @@ class MatrixSender(object): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - - def _list2byte(self, l): - byte = 0 - i = 0 - for i in range(8): - byte += 2**(7-i) if l[i] else 0 - return byte - - def _array2packet(self, a): - return [self._list2byte(a[i*8:i*8+8]) for i in range(int(len(a)/8))] - - def _intToBitlist(self,number): #convert integer number to list of bits, exampe: 1-> [0,1], 2->[1,0] - bitlistvaryinglength=[x for x in "{0:b}".format(number)] - bitlist=np.zeros(self.bitsperpixel,dtype=int) - bitlist[self.bitsperpixel-len(bitlistvaryinglength):]=bitlistvaryinglength - return bitlist - - - def send(self, image,invert=False): #changes slowly 'fadespeed'-pixels at a time + def send(self, image,invert=False): global threadrunning imgmap = [] + sendbyte=0 + senddata=bytearray() + pixelofbyte=0 for pixel in image.getdata(): r, g, b, a = pixel pixelbrightness=int( (r+g+b)/3 *(pow(2,self.bitsperpixel)-1) /255 +0.5) + sendbyte+=pixelbrightness<<(pixelofbyte*self.bitsperpixel) + pixelofbyte+=1 + if pixelofbyte>=(8/self.bitsperpixel): + pixelofbyte=0 + senddata.append(sendbyte) + sendbyte=0 - if invert: - pixelbrightness=pow(2,self.bitsperpixel)-1-pixelbrightness - - for b in self._intToBitlist(pixelbrightness): - imgmap.append(b) - - self.sendPacket(imgmap) #send packet and save last-imagemap - - - def sendPacket(self, imgmap): - packet = self._array2packet(imgmap) - self._sock.sendto(bytes(packet), (self._udphost, self._udpport)) - - - def send_bytes(self, img): - imgmap = [] - for pixel in img: - if pixel == "1": - imgmap.append(1) - else: - imgmap.append(0) - - if len(img) < 1280: - imgmap = np.hstack((imgmap, np.zeros(1280-len(img), dtype=int))) - - packet = self._array2packet(imgmap) - - self._sock.sendto(bytes(packet), (self._udphost, self._udpport)) + self._sock.sendto(bytes(senddata), (self._udphost, self._udpport)) diff --git a/client_sw/matrixsimulator.py b/client_sw/matrixsimulator.py index 45fefbd..7f19007 100644 --- a/client_sw/matrixsimulator.py +++ b/client_sw/matrixsimulator.py @@ -17,6 +17,8 @@ DEBUG=True BITSPERPIXEL=2 + + class FlipdotSim(): def __init__(self, imageSize = (160,48), #80,16 @@ -30,82 +32,30 @@ class FlipdotSim(): self.udpHostSocket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self.udpHostSocket.bind(("", self.udpPort)) - self.timesincelastpacket=time.time() - self.timelastcalculation=0 + self.time_receivedpacket=0 + + self.bitsneeded=self.imageSize[0]*self.imageSize[1]*self.bitsperpixel + def run(self): self.RunServer() - def bitlistToInt(self,bitlist): #lsb last, [1,0]=2, [0,1]=1 - number=0 - for bitindex,bitvalue in enumerate(reversed(bitlist)): - number+=bitvalue*pow(2,bitindex) - return number def RunServer(self): try: while True: - #rawData = self.udpHostSocket.recv(4096) rawData = self.udpHostSocket.recv(4096*2) + _fps=1/(time.time()-self.time_receivedpacket) + self.time_receivedpacket=time.time() - imageArray = ImageArrayAdapter().convertPacketToImageArray(rawData) - if DEBUG: - print("Received Data. Time since last message: "+str( round( time.time()-self.timesincelastpacket,2))+"s ("+str( round( (1/ (time.time()-self.timesincelastpacket) ) ,2) ) +" FPS), last calctime="+str(round( (self.timelastcalculation) ,2))) - self.timesincelastpacket=time.time() - _bitsneeded=self.imageSize[0]*self.imageSize[1]*self.bitsperpixel - if len(imageArray) < _bitsneeded: - if DEBUG: - print("WARNING: Not enough data received. Got "+str(len(imageArray))+" bits, needs "+str(_bitsneeded)+" bits") - emptyArray=np.zeros(_bitsneeded,dtype=int) - emptyArray[0:len(imageArray)]=imageArray - imageArray=emptyArray - elif len(imageArray) > _bitsneeded: - if DEBUG: - print("WARNING: Too much data received. Got "+str(len(imageArray))+" bits, needs "+str(_bitsneeded)+" bits") - imageArray=imageArray[0:_bitsneeded] + self.flipdotMatrixSimulatorWidget.showFromRawData(rawData) #send to simulator display - imageArray=[self.bitlistToInt(x) for x in np.array(imageArray).reshape((int(len(imageArray)/self.bitsperpixel),self.bitsperpixel))] - - self.flipdotMatrixSimulatorWidget.show(imageArray) #send to simulator display - - self.timelastcalculation=time.time()-self.timesincelastpacket #calculate time it took for calculation and drawing + calctime=time.time()-self.time_receivedpacket + print(str(round(calctime,4))+"s, maxFPS="+str(round(1/calctime,2))+", actual FPS="+str(round(_fps,2))) #calculate time it took for calculation and drawing finally: self.udpHostSocket.close() -class ImageArrayAdapter(): - def __init__(self): - self.arrayOfBinaryInts = [] - - def convertPacketToImageArray(self, udpPacketStr): - self.arrayOfBinaryInts = [] - byteArray = bytearray(udpPacketStr) - - #Fix for other format. Not Used - #byteArray = udpPacketStr.translate(None, b'\r\n').decode().replace('[','').replace(']','').replace(' ','').split(',') - #byteArray = [int(x) for x in byteArray] - #print("rawtype="+str(type(byteArray))) - - #print(byteArray) - for byte in byteArray: - #print("byte:"+str(byte)) - self.__appendByteToArrayOfBinaryInts(byte) - return self.arrayOfBinaryInts - - def __appendByteToArrayOfBinaryInts(self, byte): - byteValue = int(byte) - for i in range(8): - if math.floor(byteValue/(2**(7-i))) > 0: - self.arrayOfBinaryInts.append(1) - #print("append 1") - else: - self.arrayOfBinaryInts.append(0) - #print("append 0") - byteValue = byteValue%(2**(7-i)) - - - - class FlipdotMatrixSimulatorWidget(): BLACKCOLOR = 0 WHITECOLOR = 1 @@ -119,11 +69,19 @@ class FlipdotMatrixSimulatorWidget(): pygame.init() self.screen = pygame.display.set_mode((imageSize[0]*pixelSize, imageSize[1]*pixelSize)) - self.screen.fill((255,255,255)) + + self.screen.fill((0,0,0)) + self.pixelsurface=None _thread.start_new_thread(self.watchCloseThread, ()) self.bitsperpixel=bitsperpixel + minimumbrightness=32 + self.colorTable=[] + for i in range(pow(2,self.bitsperpixel)): + brightness=i/pow(2,self.bitsperpixel) + self.colorTable.append( ( int( (255-minimumbrightness)*brightness+minimumbrightness) , int(127*brightness+ minimumbrightness/2), int(minimumbrightness/4) ) ) + def watchCloseThread(self): @@ -134,29 +92,23 @@ class FlipdotMatrixSimulatorWidget(): os.kill(os.getpid(), 9) pygame.time.delay(500) - def show(self, imageArray): - for yValue in range(self.imageSize[1]): - for xValue in range(self.imageSize[0]): - i = self.imageSize[0]*yValue + xValue - color = imageArray[i] / (pow(2,self.bitsperpixel)-1) #gives a number between 0 and 1 inclusive. Example for bitsperpixel=2: [1,0]-> 2/ (2^2-1)=2/3 - self.updatePixel(xValue, yValue, color) + def showFromRawData(self, rawData): + x=0 #pixel x position + y=0 #pixel y position + bitshifts=[x*self.bitsperpixel for x in range(int(8/self.bitsperpixel))] + bitmask=int('00000011', 2) + + for cbyte in rawData: + for numbershifts in bitshifts: + pixelbyte = ( cbyte & (bitmask<> numbershifts + + pygame.draw.circle(self.screen, self.colorTable[pixelbyte], (int(x*self.pixelSize+self.pixelSize/2), int(y*self.pixelSize+self.pixelSize/2)), int(self.pixelSize/2)) #Draw LED as filled circles + + x+=1 #next pixel + y+=int(x/self.imageSize[0]) #next column if end of row + x%=self.imageSize[0] #start at first column if end of row + pygame.display.update() - def clearPixels(self): - for xValue in range(self.imageSize[0]): - for yValue in range(self.imageSize[1]): - self.updatePixel(xValue, yValue, self.BLACKCOLOR) - - def updatePixel(self, xValue, yValue, brightness): - surface = pygame.Surface((self.pixelSize, self.pixelSize)) - minimumbrightness=32 - rectcolor = ( (255-minimumbrightness)*brightness+minimumbrightness,127*brightness+ minimumbrightness/2, minimumbrightness/4) - - #surface.fill(rectcolor) - pygame.draw.circle(surface, rectcolor, (int(self.pixelSize/2), int(self.pixelSize/2)), int(self.pixelSize/2)) #Draw LED as filled circles - self.screen.blit(surface, (xValue*self.pixelSize, yValue*self.pixelSize)) - - - if __name__ == '__main__': FlipdotSim(imageSize=(WIDTH,HEIGHT), pixelSize = PIXELSIZE, udpPort=UDPPORT, bitsperpixel=BITSPERPIXEL).run() diff --git a/client_sw/matrixsimulator_processing/matrixsimulator_processing.pde b/client_sw/matrixsimulator_processing/matrixsimulator_processing.pde new file mode 100644 index 0000000..1ed8bab --- /dev/null +++ b/client_sw/matrixsimulator_processing/matrixsimulator_processing.pde @@ -0,0 +1,109 @@ +import hypermedia.net.*; + +int pixelSize = 4; +int bitPerPixel = 2; +int width=160; +int height=144; +int windowWidth=width*pixelSize; +int windowHeight=height*pixelSize; + +UDP udp; + +byte[] emptyByteArray = new byte[0]; +byte receivedData[]=emptyByteArray; +byte imagebuffer[][]=new byte[width][height]; + +PImage led[]=new PImage[(int)pow(2,bitPerPixel)]; + +void settings() { + size(width*pixelSize, height*pixelSize,P2D); +} + +void setup() { + //Setup Graphics + + frameRate(30); + noSmooth(); + background(0); + noStroke(); + + //UDP + udp = new UDP( this, 2323 ); + //udp.log( true ); // <-- printout the connection activit + udp.listen( true ); + + //Precompute LED Images + for (byte b=0;b1){ + brightnessMultiplier=1; + } + + colorR*=brightnessMultiplier; + colorG*=brightnessMultiplier; + colorB*=brightnessMultiplier; + + led[b].pixels[py*pixelSize+px] = color(colorR,colorG,colorB); + } + } + led[b].updatePixels(); + } +} + +//draw event handler +void draw() { + //map byte data to imagebuffer + if (receivedData.length > 0){ //new data? + byte[] cachedData=receivedData; //cache data to free array for new data + receivedData = emptyByteArray; + + for (int i=0;i> (shift*bitPerPixel); //mask relevant bits for current pixel + + imagebuffer[x][y]=(byte)value; //write pixel value to image buffer + } + } + + } + + clear(); //clear screen + + //int timeA=millis(); + + //Draw leds + for (int x = 0; x < imagebuffer.length; x++){ + for (int y = 0; y < imagebuffer[x].length; y++){ + byte pixel=imagebuffer[x][y]; + + //fill(255/3*pixel,127/3*pixel,0); //set pixel color + //ellipse(x*pixelSize+pixelSize/2, y*pixelSize+pixelSize/2, pixelSize, pixelSize); //Very slow + //rect(x*pixelSize, y*pixelSize, pixelSize, pixelSize); //works fast enough + + image(led[pixel], x*pixelSize, y*pixelSize); //draw precomputed led image, as fast as rect but looks nicer + } + } + + //int timeB=millis()-timeA; + //println("Time="+(timeB)); + + +} + +//udp receive event handler +void receive( byte[] data, String ip, int port ) { + receivedData=data; //received data to buffer +} \ No newline at end of file diff --git a/client_sw/movingball_example.py b/client_sw/movingball_example.py index ed2bff4..e61b9e6 100644 --- a/client_sw/movingball_example.py +++ b/client_sw/movingball_example.py @@ -9,14 +9,17 @@ matrix = MatrixSender(udphost="localhost", udpport=2323, img_size=(160,24*6), bi if __name__ == '__main__': + _fps=0 + _time_startsending=0 + im = Image.new("RGBA", matrix._img_size, MatrixSender.C_BLACK) #create image draw = ImageDraw.Draw(im) #get draw instance - ball_pos=np.array((20,20)) ball_size=10 ball_vel=np.array((-5,5)) + while(True): #------- Update Movements ------ #move ball @@ -34,7 +37,11 @@ if __name__ == '__main__': #draw ball draw.ellipse((ball_pos[0]-ball_size/2,ball_pos[1]-ball_size/2,ball_pos[0]+ball_size/2,ball_pos[1]+ball_size/2,),fill=(255,255,255)) - + _fps=1/(time.time()-_time_startsending) + _time_startsending=time.time() matrix.send(im) #construct udp packet and send to matrix + _time_endsending=time.time() - time.sleep(.1) #wait + print("Sendtime="+str(round(_time_endsending-_time_startsending,4))+"s, maxFPS="+str(round(1/(_time_endsending-_time_startsending),2))+", actual FPS="+str(round(_fps,2))) + + #time.sleep(.1) #wait