sync with current state

This commit is contained in:
T 2024-10-28 18:10:03 +01:00
parent 895a744873
commit 8dc0480c18
38 changed files with 1675 additions and 104 deletions

Binary file not shown.

38
apps/backlight.py Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import os
import sys
import random
import time
import math
import numpy as np
# Groesse des Bildschirms bestimmen
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
# Bestimme den Parameter
time_ms = 10
try:
time_ms = int(sys.argv[3])
except:
pass
# Puffer fuer Pixel erstellen und mit 0 initialisieren
curPixel = 0
phase = np.random.uniform(0, 2*np.pi, (Ny, Nx))
t = 0
f_min = 0.08
f_max = 0.10
f = np.random.uniform(f_min, f_max, (Ny, Nx))
while True:
t += time_ms/1000.0
s = 0.80 +0.2*np.sin(phase+2*np.pi*t*f)
img = np.zeros([Ny, Nx, 4])
img[:,:,3] = 255*s
# Zeige den Puffer an
out = img.reshape((Nx*Ny*4,)).astype(np.uint8)
os.write(1, out.tobytes())
# warte time_ms ms
time.sleep(time_ms*0.001)

BIN
apps/bimood Executable file

Binary file not shown.

6
apps/c-src/99-fbdev.conf Normal file
View File

@ -0,0 +1,6 @@
Section "Device"
Identifier "myfb"
Driver "fbdev"
Option "fbdev" "/dev/fb0"
EndSection

View File

@ -6,7 +6,7 @@
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <threads.h>
#include <pthread.h>
#include <stdlib.h>
int Nx, Ny;
@ -156,7 +156,7 @@ int PFT_HandlePixelflutMessage(uint8_t *buffer, int size, int sock){
return begin-(char*)buffer;
}
int PFT_HandleClient(void* sock_){
void* PFT_HandleClient(void* sock_){
int sock = (int)(size_t)sock_;
PFT_ClientThreadCount++;
uint8_t buf[1024];
@ -172,7 +172,7 @@ int PFT_HandleClient(void* sock_){
}
int PFT_RunServer(void* _){
void* PFT_RunServer(void* _){
int client_sock;
socklen_t addr_len;
struct sockaddr_in addr;
@ -220,9 +220,9 @@ int PFT_RunServer(void* _){
while(1){
client_sock = accept(server_sock, (struct sockaddr*)&addr, &addr_len);
if(client_sock > 0){
thrd_t thread;
thrd_create(&thread, PFT_HandleClient, (void*)(size_t)client_sock);
thrd_detach(thread);
pthread_t thread;
pthread_create(&thread, NULL, PFT_HandleClient, (void*)(size_t)client_sock);
pthread_detach(thread);
}
}
close(server_sock);
@ -235,8 +235,8 @@ int main(int argc, char **argv) {
buf = (uint8_t*)malloc(Nx*Ny*3);
port = atoi(argv[3]);
if (port == 0) port = 4444;
thrd_t thread;
thrd_create(&thread, PFT_RunServer, NULL);
pthread_t thread;
pthread_create(&thread, NULL, PFT_RunServer, NULL);

BIN
apps/cnoise Executable file

Binary file not shown.

61
apps/convergence.py Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
import numpy as np
import time
import os
import sys
import colorsys
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
N = max(Nx, Ny)
def CpxRandNormal(min, max, N):
mu = (min+max)/2
sigma = (max-min)/2
return np.random.normal(mu, sigma, (N, N))+1.0j*np.random.normal(mu, sigma, (N, N))
def CpxRand(min, max, N):
return np.random.uniform(min, max, (N, N))+1.0j*np.random.uniform(min, max, (N, N))
A = CpxRandNormal(-100, 100, N)
B = CpxRand(-1, 1, N)*0.1
C = CpxRand(-1, 1, N)
A_ex = np.linalg.inv(np.eye(N)-B)@C
dir = -1
solve = True
t = 0
while True:
t += 0.003
if solve:
A = 0.9*A+0.1*(B@A+C)
else:
A += CpxRandNormal(0, 0.008, N)
diff = A-A_ex
#angles = (np.angle(diff)+np.pi)/(2*np.pi
#diff = np.abs(diff)
#diff = 1-diff/(1+diff)
a = np.abs(np.real(diff))
a = a/(a+1)
b = np.abs(np.imag(diff))
b = b/(b+1)
img = np.zeros((Ny, Nx, 3))
for x in range(Nx):
for y in range(Ny):
c = colorsys.hsv_to_rgb((t+0.2+a[x, y]*0.2)%1, 1, 0.5*b[x, y]+0.5)
img[y,x,:] = np.array(c)*255
#img[:,:,0] = np.minimum(np.abs(diff*255).astype(int), 255)[:Ny,:Nx]
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
#A += np.random.uniform(-0.01, 0.01, (N, N))
#print(len(out))
os.write(1, out.tobytes())
if solve and np.sum(np.abs(diff)) <= 0.001*N*N:
solve = False
elif not solve and np.sum(np.abs(diff)) >= 1*N*N:
solve = True
#os.write(2, b"frame")
#print(angles, diff)
time.sleep(0.01)

110
apps/digi_clock.py Executable file
View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
import pygame
from datetime import datetime
import math
# Displaygröße
Nx = int(sys.argv[1]) # Breite des Displays (z.B. 80)
Ny = int(sys.argv[2]) # Höhe des Displays (z.B. 40)
# Initialize pygame
pygame.init()
pygame.font.init()
# Pfad zur Pixelfont im Verzeichnis "apps"
font_path = os.path.join('.', 'PressStart2P-Regular.ttf') # Pfad zur Schriftart
# Schriftart mit optimierter Größe laden (z.B. 16px bis 20px)
font_size = 16 # Geeignete Schriftgröße wählen
font = pygame.font.Font(font_path, font_size)
clock = pygame.time.Clock()
time_elapsed = 0 # Zeit-Tracker für den Farbverlauf
# Feste Farbe für den Rand (Türkis)
border_color = (64, 224, 208) # RGB-Werte für Türkis
# Funktion zur Berechnung der RGB-Farbe basierend auf der Zeit und der Position des Zeichens
def calculate_color(time_elapsed, position, total_length):
"""Berechnet eine RGB-Farbe basierend auf der verstrichenen Zeit und der Position des Zeichens."""
frequency = 2 * math.pi / total_length # Frequenz für den Regenbogenverlauf
phase_shift = position * frequency # Verschiebung der Phase pro Zeichen
r = int((math.sin(time_elapsed + phase_shift) + 1) * 127.5)
g = int((math.sin(time_elapsed + phase_shift + 2 * math.pi / 3) + 1) * 127.5)
b = int((math.sin(time_elapsed + phase_shift + 4 * math.pi / 3) + 1) * 127.5)
return (r, g, b)
# Funktion zum Rendern des Rahmens
def render_border(display_surface, Nx, Ny, border_color):
"""Zeichnet den Rahmen mit einer festen Farbe."""
# Obere Linie des Rahmens
for x in range(Nx):
display_surface.set_at((x, 0), border_color) # Oben
# Rechte Linie des Rahmens
for y in range(1, Ny - 1):
display_surface.set_at((Nx - 1, y), border_color) # Rechts
# Untere Linie des Rahmens
for x in range(Nx - 1, -1, -1):
display_surface.set_at((x, Ny - 1), border_color) # Unten
# Linke Linie des Rahmens
for y in range(Ny - 2, 0, -1):
display_surface.set_at((0, y), border_color) # Links
# Funktion zum Zeichnen der Uhrzeit
def render_time(display_surface, Nx, Ny, font, current_time, time_elapsed):
"""Zeichnet die aktuelle Uhrzeit mittig auf das Display."""
# Reduzierter Zeichenabstand (85% der Schriftgröße)
character_spacing = font_size * 0.95
# Breite des gesamten Textes berechnen
text_width = len(current_time) * character_spacing
# Horizontale und vertikale Zentrierung
x_offset = (Nx - text_width) // 2
y_offset = (Ny - font_size) // 2
# Zeichnen der Uhrzeit
for i, char in enumerate(current_time):
# Berechne die Farbe für das aktuelle Zeichen
text_color = calculate_color(time_elapsed, i, len(current_time))
# Rendern des Zeichens
text = font.render(char, True, text_color)
text_rect = text.get_rect(topleft=(x_offset + i * character_spacing, y_offset))
# Zeichen auf das Display zeichnen
display_surface.blit(text, text_rect)
# Hauptprogramm zur Anzeige der Uhrzeit
while True:
# Zeit seit Beginn des Programms
time_elapsed += clock.get_time() / 1000.0 # in Sekunden
# Aktuelle Zeit abrufen
now = datetime.now()
current_time = now.strftime("%H:%M") # Uhrzeit im Format HH:MM
# Ein leeres Display für die Uhrzeit erstellen
display_surface = pygame.Surface((Nx, Ny))
display_surface.fill((0, 0, 0)) # Hintergrund schwarz
# Rahmen zeichnen
render_border(display_surface, Nx, Ny, border_color)
# Uhrzeit zeichnen (Aufruf der neuen Funktion)
render_time(display_surface, Nx, Ny, font, current_time, time_elapsed)
# Sicherstellen, dass das Bild die korrekten Dimensionen hat und der Framebuffer vollständig ist
out = pygame.surfarray.array3d(display_surface).transpose((1, 0, 2)).astype(np.uint8).tobytes()
# Framebuffer zum Server senden
os.write(1, out)
clock.tick(30) # 30 FPS für flüssigen Farbverlauf

101
apps/doom_fire_psx2.py Executable file
View File

@ -0,0 +1,101 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
import pygame
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
try:
fps = float(sys.argv[3])
except (IndexError, ValueError):
fps = 15
#fps = 15
colors = np.array([
0x0, 0x0, 0x0,
# 0x07,0x07,0x07,
# 0x1F,0x07,0x07,
# 0x2F,0x0F,0x07,
# 0x47,0x0F,0x07,
0x57,0x17,0x07,
# 0x67,0x1F,0x07,
# 0x77,0x1F,0x07,
# 0x8F,0x27,0x07,
0x9F,0x2F,0x07,
# 0xAF,0x3F,0x07,
# 0xBF,0x47,0x07,
# 0xC7,0x47,0x07,
0xDF,0x4F,0x07,
# 0xDF,0x57,0x07,
# 0xDF,0x57,0x07,
# 0xD7,0x5F,0x07,
# 0xD7,0x5F,0x07,
# 0xD7,0x67,0x0F,
# 0xCF,0x6F,0x0F,
# 0xCF,0x77,0x0F,
0xCF,0x7F,0x0F,
# 0xCF,0x87,0x17,
# 0xC7,0x87,0x17,
# 0xC7,0x8F,0x17,
# 0xC7,0x97,0x1F,
# 0xBF,0x9F,0x1F,
# 0xBF,0x9F,0x1F,
# 0xBF,0xA7,0x27,
0xBF,0xA7,0x27,
# 0xBF,0xAF,0x2F,
# 0xB7,0xAF,0x2F,
# 0xB7,0xB7,0x2F,
# 0xB7,0xB7,0x37,
# 0xCF,0xCF,0x6F,
# 0xDF,0xDF,0x9F,
# 0xEF,0xEF,0xC7,
# 0xFF,0xFF,0xFF
], dtype=np.uint8)
colors = np.reshape(colors, (colors.size//3, 3))
grid = np.zeros((Ny,Nx), dtype=np.int)
gridout = np.zeros((Ny,Nx), dtype=np.int)
grid[0,:].fill(colors.shape[0]-1)
clock = pygame.time.Clock()
def state2red(c):
return colors[c, 0]
def state2green(c):
return colors[c, 1]
def state2blue(c):
return colors[c, 2]
i = 0
while True:
i = i + 1
rnd = np.random.randint(3, size=(Ny, Nx))
for px in range(Nx):
for py in range(1, Ny):
#grid[py, px] = max(grid[py-1, px] - 1, 0) # v1
#grid[py, px] = max(grid[py-1, px] - (rnd[py, px] & 1), 0) # v2
grid[py, (px - rnd[py, px] + 1) % Nx] = max(grid[py-1, px] - (rnd[py, px] & 1), 0) # v3
#if i % 100 == 0:
# grid[0,:].fill(0)
#elif (i - (colors.shape[0] +10)) % 100 == 0:
# grid[0,:].fill(colors.shape[0]-1)
gridup = np.flipud(np.fliplr(grid))
np.clip(grid + gridup, 0, colors.shape[0]-1, out=gridout)
#out = np.dstack((state2red(gridout), state2green(gridout), state2blue(gridout), np.full((Ny, Nx), 100, dtype=np.uint8))).astype(np.uint8).tobytes()
out = np.dstack((state2red(gridout), state2green(gridout), state2blue(gridout))).astype(np.uint8).tobytes()
os.write(1, out)
clock.tick_busy_loop(fps)

BIN
apps/dualmaze Executable file

Binary file not shown.

68
apps/example2.py Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import os
import sys
import random
import time
# Groesse des Bildschirms bestimmen
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
# Bestimme den Parameter
time_ms = 100
try:
time_ms = int(sys.argv[3])
except:
pass
# Puffer fuer Pixel erstellen und mit 0 initialisieren
buffer = bytearray(b"\x00" * (4 * Nx * Ny))
curPixel = 0
while True:
# Zufaellige Pixel waeheln
# rot
x_r = random.randint(0, Nx-1)
y_r = random.randint(0, Ny-1)
i_r = 4*(x_r+Nx*y_r)
# gruen
x_g = random.randint(0, Nx-1)
y_g = random.randint(0, Ny-1)
i_g = 4*(x_g+Nx*y_g)
# blau
x_b = random.randint(0, Nx-1)
y_b = random.randint(0, Ny-1)
i_b = 4*(x_b+Nx*y_b)
# weiss
x_w = random.randint(0, Nx-1)
y_w = random.randint(0, Ny-1)
i_w = 4*(x_w+Nx*y_w)
# Pixel in Puffer schreiben
# rot
buffer[i_r+0] = 0xff # Rotanteil
buffer[i_r+1] = 0x00 # Gruenanteil
buffer[i_r+2] = 0x00 # Blauanteil
buffer[i_r+3] = 0x00
# gruen
buffer[i_g+0] = 0x00 # Rotanteil
buffer[i_g+1] = 0xff # Gruenanteil
buffer[i_g+2] = 0x00 # Blauanteil
buffer[i_g+3] = 0x00
# blau
buffer[i_b+0] = 0x00 # Rotanteil
buffer[i_b+1] = 0x00 # Gruenanteil
buffer[i_b+2] = 0xff # Blauanteil
buffer[i_b+3] = 0x00
# weiss
buffer[i_w+0] = 0x00 # Rotanteil
buffer[i_w+1] = 0x00 # Gruenanteil
buffer[i_w+2] = 0x00 # Blauanteil
buffer[i_w+3] = 0xff
# Zeige den Puffer an
os.write(1, buffer)
# warte time_ms ms
time.sleep(time_ms*0.001)

42
apps/fading_pixels.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
import pygame
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
try:
fps = float(sys.argv[3])
except (IndexError, ValueError):
fps = 1.0
def point2idx(x,y):
return 3*(x+Nx*y)
def set_pixel_max(buffer, x, y):
idx = point2idx(x,y)
for i in range(3):
buffer[idx+i] = max(0x00, buffer[idx+i]-1)
def set_pixel_rand(buffer, x, y):
idx = point2idx(x,y)
for i in range(3):
buffer[idx+i] = np.random.randint(0x00, 0xff)
clock = pygame.time.Clock()
buffer = bytearray(b"\x00"*(3*Nx*Ny))
i = 0
while True:
i = i + 1
for px in range(Nx):
for py in range(Ny):
set_pixel_max(buffer, px, py)
for j in range(10):
set_pixel_rand(buffer, np.random.randint(0, Nx), np.random.randint(0, Ny))
if i > 100:
os.write(1, buffer)
clock.tick_busy_loop(fps)

BIN
apps/fbcp Executable file

Binary file not shown.

78
apps/fdtd.py Executable file
View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# From the Meep tutorial: plotting permittivity and fields of a bent waveguide
from __future__ import division
import meep as mp
#import matplotlib.pyplot as plt
import os
import sys
import numpy as np
from matplotlib import cm
import pygame
#mp.quiet(True)
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
try:
fps = float(sys.argv[3])
except (IndexError, ValueError):
fps = 40
cell = mp.Vector3(8,4,0)
geometry = [mp.Block(mp.Vector3(12,1,mp.inf),
center=mp.Vector3(0,0),
material=mp.Medium(epsilon=24))
]#,
#mp.Block(mp.Vector3(1,5,mp.inf),
# center=mp.Vector3(1,1),
# material=mp.Medium(epsilon=12))]
#pml_layers = [mp.PML(2.0)]
resolution = 10
sources = [mp.Source(mp.ContinuousSource(frequency=0.15, width=20), # wavelength=2*(11**0.5)
component=mp.Ez,
center=mp.Vector3(0,0),
size=mp.Vector3(0,1))
]
sim = mp.Simulation(cell_size=cell,
geometry=geometry,
sources=sources,
resolution=resolution)
buffer = np.empty([80,40])
clock = pygame.time.Clock()
def get_slice(sim):
val = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Ez, arr=buffer)
#plt.figure()
#plt.imshow(val, interpolation='none', cmap='RdBu')
#plt.axis('off')
#plt.show()
out = (val - np.amin(val)) / (np.amax(val) - np.amin(val))
out = np.transpose(out) # or switch x and y
out = out.ravel()
out = 250*cm.RdBu(out)
out = out[:,0:3]
out = out.ravel()
out = out.astype(np.uint8).tobytes()
os.write(1, out)
clock.tick_busy_loop(fps)
def plot_dielectric(sim):
val = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric, arr=buffer)
#plt.figure()
#plt.imshow(val, interpolation='none', cmap='RdBu')
#plt.axis('off')
#plt.show()
#print()
#sim.run(mp.at_beginning(plot_dielectric), mp.at_every(0.6, get_slice), until=200)
sim.run(mp.at_every(0.00001, get_slice), until=1000)

109
apps/fibonacci-clock.py Executable file
View File

@ -0,0 +1,109 @@
#!/usr/bin/env python3
import sys
import os
from time import strftime
import time
from math import ceil
#Nx = int(sys.argv[1])
#Ny = int(sys.argv[2])
#param = sys.argv[3]
Nx = 80
Ny = 40
buffer = bytearray(b"\x00"*(3*Nx*Ny))
def SetPixel(x, y, r, g, b):
idx = 3*(x+Nx*y)
buffer[idx+0] = r
buffer[idx+1] = g
buffer[idx+2] = b
def two(r,g,b):
for y in range (1,15):
for x in range (9,23):
SetPixel(x,y,r,g,b)
def three(r,g,b):
for y in range (17,39):
for x in range (9,31):
SetPixel(x,y,r,g,b)
def one(r,g,b):
for y in range (1,7):
for x in range (25,31):
SetPixel(x,y,r,g,b)
def one_one(r,g,b):
for y in range (9,15):
for x in range (25,31):
SetPixel(x,y,r,g,b)
def five(r,g,b):
for y in range (1,39):
for x in range (33,71):
SetPixel(x,y,r,g,b)
def and_gate(hourcol, mincol):
if hourcol and mincol:
return 'y'
if hourcol:
return 'r'
if mincol:
return 'g'
if not hourcol and not mincol:
return 'b'
def get_colsquares(hour, minute):
coldict = {
0: [0, 0, 0, 0, 0],
1: [0, 0, 0, 0, 1],
2: [0, 0, 0, 1, 1],
3: [0, 0, 1, 1, 0],
4: [0, 0, 1, 1, 1],
5: [0, 1, 1, 0, 0],
6: [0, 1, 1, 1, 0],
7: [0, 1, 1, 1, 1],
8: [1, 1, 0, 0, 0],
9: [1, 1, 0, 1, 0],
10: [1, 1, 1, 0, 0],
11: [1, 1, 1, 1, 0],
12: [1, 1, 1, 1, 1]
}
i = 0
result = []
while i < 5:
result.append( and_gate(coldict[hour][i], coldict[minute][i]) )
i += 1
return result
def gettime():
hour = int(strftime('%I'))
minute12 = ceil(int(strftime('%M'))/5.0)
minute = int(strftime('%M'))
return hour, minute12, minute
while True:
rgbw = {
'r' : (255, 0, 0),
'g' : (0, 255, 0),
'b' : (0, 0, 255),
'y' : (255, 255, 0)
}
hour, minute12, minute = gettime()
colsq = get_colsquares(hour, minute12)
five(rgbw[colsq[0]][0],rgbw[colsq[0]][1],rgbw[colsq[0]][2])
three(rgbw[colsq[1]][0],rgbw[colsq[1]][1],rgbw[colsq[1]][2])
two(rgbw[colsq[2]][0],rgbw[colsq[2]][1],rgbw[colsq[2]][2])
one_one(rgbw[colsq[3]][0],rgbw[colsq[3]][1],rgbw[colsq[3]][2])
one(rgbw[colsq[4]][0],rgbw[colsq[4]][1],rgbw[colsq[4]][2])
os.write(1, buffer)
time.sleep(1)
#grid()

BIN
apps/flicker Executable file

Binary file not shown.

View File

@ -13,7 +13,7 @@ Ny = int(sys.argv[2])
PixelsPerFrame = 20
NumLines = 7
LineGenerationRate = 0.2
time_ms = 10
time_ms = 20
try:
time_ms = int(sys.argv[3])
except:

BIN
apps/maze Executable file

Binary file not shown.

81
apps/pendlum.py Executable file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env python3
import numpy as np
import sys
import os
import time
import colorsys
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
N = 100
iterations = 2
try:
N = int(sys.argv[3])
except:
pass
def rk4(f, x, dt):
k1 = f(x)
k2 = f(x+k1*dt/2)
k3 = f(x+k2*dt/2)
k4 = f(x+k3*dt)
return x+dt*(k1+2*k2+2*k3+k4)/6.0
dt = 0.08
m = 1
l = 1
g = 1
def sq(x): return x*x
def rhs(x):
phi1 = x[0]
p1 = x[1]
phi2 = x[2]
p2 = x[3]
dphi1 = 6/(m*l*l)*(2*p1-3*np.cos(phi1-phi2)*p2)/(16-9*sq(np.cos(phi1-phi2)))
dphi2 = 6/(m*l*l)*(8*p2-3*np.cos(phi1-phi2)*p1)/(16-9*sq(np.cos(phi1-phi2)))
dp1 = -0.5*m*l*l*(dphi1*dphi2*np.sin(phi1-phi2)+3*g/l*np.sin(phi1))
dp2 = -0.5*m*l*l*(-dphi1*dphi2*np.sin(phi1-phi2)+g/l*np.sin(phi2))
return np.array([dphi1, dp1, dphi2, dp2])
s = np.array([np.pi+0.1, 0, np.pi-0.1, 0])
angles = np.linspace(np.pi, np.pi/3, N)+np.random.uniform(-np.pi/6/N, np.pi/6/N, N)
angles = np.random.uniform(-np.pi/6, np.pi/3, N)+np.pi
states = [np.array([np.pi, 0, a, 0]) for a in angles]
def clamp(x, min_, max_):
return max(min_, min(max_, x))
buffer = bytearray(b"\x00"*Nx*Ny*3)
def setpixel(x, y, r, g, b):
xi = int(Nx*(x+1.2)/2.4)
yi = int(Ny*(y+1.2)/2.4)
if xi < 0 or xi >= Nx or yi < 0 or yi >= Ny:
return
idx = xi+Nx*yi
buffer[3*idx+0] = r
buffer[3*idx+1] = g
buffer[3*idx+2] = b
timestep = 0
timefac = 0.03
while True:
timestep += 1
for s, i in zip(states, range(len(states))):
phi1 = s[0]
phi2 = s[2]
x1 = np.sin(phi1)*l
y1 = np.cos(phi1)*l
x2 = np.sin(phi2)*l+x1
y2 = np.cos(phi2)*l+y1
h = 0.2*i/N+0*timestep*timefac/iterations
r, g, b = colorsys.hsv_to_rgb(h%1, 1, 1)
setpixel(x2, y2, int(r*255), int(g*255), int(b*255))
states[i] = rk4(rhs, s, dt/iterations)
if timestep > 10*iterations and timestep % iterations == 0:
#time.sleep(0.01)
os.write(1, buffer)

BIN
apps/pixelflut Executable file

Binary file not shown.

33
apps/plane_wave.py Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
import pygame
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
try:
frequency = float(sys.argv[3])
except (IndexError, ValueError):
frequency = 5
fps = 30
amplitude = 105
offset = 150
t = 0
x,y = np.meshgrid(np.linspace(0, 2*np.pi * min(1, Nx/Ny), Nx), np.linspace(0, 2*np.pi * min(1, Ny/Nx), Ny))
kx = x + y
clock = pygame.time.Clock()
while True:
t = t + 1e-3
R = amplitude * np.sin(kx - 2 * np.pi * frequency * t) + offset
G = amplitude * np.sin(kx - 2 * np.pi * frequency * t + 2*np.pi/3) + offset
B = amplitude * np.sin(kx - 2 * np.pi * frequency * t + 4*np.pi/3) + offset
out = np.dstack((R,G,B)).astype(np.uint8).tobytes()
os.write(1, out)
clock.tick_busy_loop(fps)

BIN
apps/predprey Executable file

Binary file not shown.

82
apps/quadratic.py Executable file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env python3
import numpy as np
import os
import sys
import time
import colorsys
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
iterations = 10
class Slider:
def __init__(self, lo, hi, step, pos=0):
self.lo = lo
self.hi = hi
self.stepSize = step
self.pos = pos
self.dir = 1
def step(self):
if self.pos >= self.hi:
self.dir = -1
elif self.pos <= self.lo:
self.dir = 1
self.pos += self.dir * self.stepSize
return self.pos
Sa = Slider(-10, 10, 0.1/iterations, np.random.uniform(-2, 2))
Sb = Slider(-10, 10, 0.2/iterations, np.random.uniform(-2, 2))
Sc = Slider(-10, 10, 0.2/iterations, np.random.uniform(-2, 2))
Sd = Slider(-10, 10, 0.1/iterations, np.random.uniform(-2, 2))
Se = Slider(-10, 10, 0.2/iterations, np.random.uniform(-2, 2))
Sf = Slider(0, 6, 0.05/iterations, 0)
x, y = np.meshgrid(
np.linspace(-4, 4, Nx),
np.linspace(-4, 4, Ny))
color = 1.0
img = np.zeros( [Ny, Nx, 3] )
cr = 1.0
cg = 0.5
cb = 0.25
step = 0
while True:
a = Sa.step()
b = Sb.step()
c = Sc.step()
d = Sd.step()
e = Se.step()
f = Sf.step()
Sa.pos += b*0.001
Sb.pos += c*0.001
Sc.pos += d*0.001
Sd.pos += e*0.001
Se.pos += f*0.001
Sf.pos += a*0.001
curve = (np.abs(a*x**2 + b*x*y + c*y**2 + d*x + e*y + f) <= 1.5)*1
cr, cg, cb = colorsys.hsv_to_rgb(color, 1, 1)
img[:,:,0] = np.where(curve > 0, curve*cr*255, img[:,:,0])
img[:,:,1] = np.where(curve > 0, curve*cg*255, img[:,:,1])
img[:,:,2] = np.where(curve > 0, curve*cb*255, img[:,:,2])
step += 1
if step % iterations == 0:
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
os.write(1, out.tobytes())
time.sleep(0.01)
color = (color+0.01/iterations ) % 1

71
apps/rps.py Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
import pygame
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
try:
frequency = float(sys.argv[3])
except (IndexError, ValueError):
frequency = 5
fps = 10
nstates = 5
colors = np.linspace(0, 255, nstates, dtype=np.uint8)
red = np.random.choice(colors, size=(Ny,Nx))
green = np.zeros((Ny, Nx))
blue = np.zeros((Ny, Nx))
grid = np.dstack((red, green, blue)).astype(np.uint8)
clock = pygame.time.Clock()
def clamp(value, min, max):
if value > max:
return max
elif value < min:
return min
return value
while True:
for px in range(Nx):
for py in range(Ny):
statecount = np.zeros(nstates, dtype=np.uint8)
for ix in [-1, 0, 1]:
for iy in [-1, 0, 1]:
if (px+ix) >= Nx or (px+ix) <= 0 or (py+iy) >= Ny or (py+iy) <= 0:
i = 0
else:
i = int(grid[py + iy, px + ix, 0] / colors[1])
#i = int(grid[(px + ix) % Nx, (py + iy) % Ny, 0] / colors[1])
#i = int(grid[max(min(px + ix, Nx-1), 0), max(min(py + iy, Ny-1), 0), 0] / colors[1])
#print(px, py, max(min(px + ix, Nx-1), 0), max(min(py + iy, Ny-1), 0), file=sys.stderr)
statecount[i] = statecount[i] + 1
i = int(grid[py, px, 0] / colors[1])
i_successor = (i+1) % colors.size
if statecount[i_successor] >= 2: #(2-np.random.randint(0, 2)):
grid[py, px, 0] = colors[i_successor]
# for i in range(Nx*Ny):
# c1 = clamp(i % Nx - 1, 0, Nx - 1)
# c2 = clamp(int(i / Nx) - 1, 0, Ny - 1)
# neighbourhood = grid[c1:(c1+3), c2:(c2+3), 0]
# #print(neighbourhood.size, file=sys.stderr)
out = grid.tobytes()
os.write(1, out)
clock.tick_busy_loop(fps)

67
apps/sinic.py Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/env python3
import numpy as np
import os
import sys
import time
import colorsys
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
iterations = 2
class Slider:
def __init__(self, lo, hi, step, pos=0):
self.lo = lo
self.hi = hi
self.stepSize = step
self.pos = pos
self.dir = 1
def step(self):
if self.pos >= self.hi:
self.dir = -1
elif self.pos <= self.lo:
self.dir = 1
self.pos += self.dir * self.stepSize
return self.pos
x, y = np.meshgrid(
np.linspace(-2, 2, Nx),
np.linspace(-2, 2, Ny))
img = np.zeros( [Ny, Nx, 3] )
Sa = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
Sb = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
Sc = Slider(0, 1, 0.005, np.random.uniform(-1, 1))
Scolor = Slider(0, 1, 0.001/iterations, 0)
step = 0
while True:
a = Sa.step()
b = Sb.step()
c = Sc.step()
Sa.pos += b*0.001
Sb.pos += c*0.001
Sc.pos += a*0.001
curve = (np.clip(np.sin(c) + np.sin(np.sin(a*x) + np.cos(b*y)) - np.cos(np.sin(b*x*y) + np.cos(b*x)), 0, 1))
cr, cg, cb = colorsys.hsv_to_rgb(Scolor.step(), 1, 1)
img[:,:,0] = curve*cr*255
img[:,:,1] = curve*cg*255
img[:,:,2] = curve*cb*255
step += 1
if step % iterations == 0:
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
os.write(1, out.tobytes())
time.sleep(0.01)

68
apps/sinic2.py Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import numpy as np
import os
import sys
import time
import colorsys
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
iterations = 2
class Slider:
def __init__(self, lo, hi, step, pos=0):
self.lo = lo
self.hi = hi
self.stepSize = step
self.pos = pos
self.dir = 1
def step(self):
if self.pos >= self.hi:
self.dir = -1
elif self.pos <= self.lo:
self.dir = 1
self.pos += self.dir * self.stepSize
return self.pos
x, y = np.meshgrid(
np.linspace(-2, 2, Nx),
np.linspace(-2, 2, Ny))
img = np.zeros( [Ny, Nx, 3] )
Sa = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
Sb = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
Sc = Slider(0, 1, 0.005, np.random.uniform(-1, 1))
Scolor = Slider(0, 1, 0.001/iterations, 0)
step = 0
while True:
a = Sa.step()
b = Sb.step()
c = Sc.step()
Sa.pos += b*0.001
Sb.pos += c*0.001
Sc.pos += a*0.001
curve = np.clip(c+np.sin(a*x*x-b*y*y)-np.sin(a*x+b*y)-np.cos(a*b*x*y), 0, 1)
cr, cg, cb = colorsys.hsv_to_rgb(Scolor.step(), 1, 1)
img[:,:,0] = curve*cr*255
img[:,:,1] = curve*cg*255
img[:,:,2] = curve*cb*255
step += 1
if step % iterations == 0:
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
os.write(1, out.tobytes())
time.sleep(0.01)

View File

@ -2,24 +2,33 @@
import os
import sys
import time
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
param = 10
p1 = 20
p2 = 10
try:
param = int(sys.argv[3])
param = sys.argv[3]
p = param.split(",")
p1 = int(p[0])
p2 = int(p[1])
except:
pass
buffera = bytearray(b"\x00" * (3 * Nx * Ny))
bufferb = bytearray(b"\xFF" * (3 * Nx * Ny))
cnt = 0
os.write(2,b"Usage: Parameter: \"20,10\"\n -> updates every 20*0.01s, every 10th frame is white")
while True:
os.write(1, buffera)
time.sleep(param*0.01)
os.write(1, bufferb)
time.sleep(param*0.01)
time.sleep(p1*0.01)
if cnt == p2-1:
cnt = 0
os.write(1, bufferb)
time.sleep(p1*0.01)
cnt += 1

63
apps/swifthohenberg.py Executable file
View File

@ -0,0 +1,63 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
Nx = int(sys.argv[1])
Ny = int(sys.argv[2])
param = sys.argv[3]
buffer = bytearray(b" "*(3*Nx*Ny))
Lx = 32 #128 #Physikalische Laenge des Gebietes in x-Richtung
Ly =32 #128#Physikalische Laenge des Gebietes in y-Richtung
x, y = np.meshgrid(np.arange(Nx)* Lx/Nx, np.arange(Ny)* Ly/Ny) #x-Array
kx, ky = np.meshgrid(np.fft.fftfreq(Nx,Lx/(Nx*2.0*np.pi)), np.fft.fftfreq(Ny,Ly/(Ny*2.0*np.pi)))
ksq = kx*kx + ky*ky
c = 0.0+(np.random.random((Ny,Nx))-0.5)*0.1
eps=0.3
delta=0.0
#eps = 0.1
#delta = 1.0
t = 0.0
dt = 0.01
T_End = 300000
N_t = int(T_End / dt)
plotEveryNth = 100
ck = np.fft.fft2(c) #FFT(c)
# Lineare Terme
def rhs_lin(ksq):
result=eps-(1.0-ksq)**2
return result
Eu=1.0/(1.0-dt*rhs_lin(ksq))
i = 0
def lerp_sat(a, t, b):
v = (1-t)*a+t*b
v = int(v)
if v < 0:
v = 0
if v > 255:
v = 255
return v
while True:
i+= 1
ck=Eu*(ck+dt*(delta*np.fft.fft2(np.fft.ifft2(ck)**2)-np.fft.fft2(np.fft.ifft2(ck)**3)))
c=np.fft.ifft2(ck)
eps = 0.1+0.2*np.cos(i/10000)
delta = np.sin(i/10000)
if(i % plotEveryNth == 0):
myc = c.real
myc = (myc-np.min(myc))/(np.max(myc)-np.min(myc))
for px in range(Nx):
for py in range(Ny):
idx = 3*(px+Nx*py)
buffer[idx+0] = lerp_sat(0xff, myc[py,px], 0x00)
buffer[idx+1] = lerp_sat(0x00, myc[py,px], 0xff)
buffer[idx+2] = 0
os.write(1, buffer)

111
apps/textscroll.py Executable file
View File

@ -0,0 +1,111 @@
#!/usr/bin/env python3
import os
import sys
import pygame
import paho.mqtt.client as mqtt
import random
class ScrollingTextDisplay:
def __init__(self, width: int, height: int):
pygame.init()
self.width = width
self.height = height
self.screen = pygame.Surface((width, height))
self.clock = pygame.time.Clock()
# Liste der Standardtexte
self.default_texts = [
"Text per MQTT an: mqtt.chaospott.de Topic: test/topic ", # Hardcodierte IP-Adresse
"Willkommen beim MQTT-Display!",
"Aktuelle Nachrichten hier empfangen.",
"MQTT-Server verbindet...",
"Textnachricht anzeigen...",
]
self.error_text = "MQTT-Server nicht erreichbar!"
self.text = self.get_random_default_text() # Zufälligen Standardtext auswählen
self.text_x = width
# Schriftart 'PressStart2P-Regular' aus dem Verzeichnis 'apps' laden
self.font = pygame.font.Font("PressStart2P-Regular.ttf", 16) # Schriftgröße anpassen
# Fest codierte MQTT-Einstellungen
self.mqtt_broker = "mqtt.chaospott.de" # Beispiel IP-Adresse des MQTT-Brokers
self.topic = "test/topic" # Beispiel Topic
self.mqtt_client = mqtt.Client()
self.mqtt_client.on_connect = self.on_connect
self.mqtt_client.on_message = self.on_message
self.mqtt_client.on_disconnect = self.on_disconnect
# Versuche die Verbindung aufzubauen
self.connect_to_mqtt()
# Zähler für die angezeigte Nachricht
self.message_display_count = 0
def get_random_default_text(self):
"""Wählt zufällig einen Standardtext aus der Liste aus."""
return random.choice(self.default_texts)
def connect_to_mqtt(self):
try:
self.mqtt_client.connect(self.mqtt_broker)
self.mqtt_client.loop_start()
except Exception as e:
print(f"Verbindungsfehler zu MQTT-Server: {e}")
self.text = self.error_text # Setze Fehlertext für die Anzeige
self.text_x = self.width # Text von rechts starten
def on_connect(self, client, userdata, flags, rc):
if rc == 0:
client.subscribe(self.topic)
self.text = self.get_random_default_text() # Setze einen zufälligen Standardtext
else:
print(f"Fehler beim Verbinden mit MQTT: Code {rc}")
self.text = self.error_text
def on_disconnect(self, client, userdata, rc):
if rc != 0:
print("Verbindung zum MQTT-Server verloren. Erneuter Verbindungsversuch...")
self.text = self.error_text
self.text_x = self.width # Fehlertext von rechts starten
# Erneut die Verbindung aufbauen
self.connect_to_mqtt()
def on_message(self, client, userdata, msg):
self.text = msg.payload.decode("utf-8") if msg.payload.decode("utf-8") else self.get_random_default_text()
self.text_x = self.width # Text von rechts starten
self.message_display_count = 3 # Setze den Zähler auf 3
def draw_text(self):
self.screen.fill((0, 0, 0)) # Bildschirm leeren
text_surface = self.font.render(self.text, True, (255, 255, 255))
text_width = text_surface.get_width() # Breite des Textes ermitteln
text_y = (self.height - text_surface.get_height()) // 2 # Y-Position zentrieren
self.screen.blit(text_surface, (self.text_x, text_y)) # Text zentriert zeichnen
def display(self):
while True:
text_surface = self.font.render(self.text, True, (255, 255, 255))
text_width = text_surface.get_width() # Breite des Textes ermitteln
self.text_x -= 1 # Text nach links verschieben
if self.text_x < -text_width: # Wenn der Text komplett verschwunden ist
self.text_x = self.width # Text von rechts neu starten
# Falls der Text leer ist, setze Standardtext oder Fehlertext
if self.message_display_count > 0:
self.message_display_count -= 1
else:
self.text = self.get_random_default_text() # Setze zurück auf zufälligen Standardtext
self.draw_text()
os.write(1, pygame.image.tostring(self.screen, "RGB"))
self.clock.tick(30)
if __name__ == "__main__":
Nx = int(sys.argv[1]) # Breite des Displays
Ny = int(sys.argv[2]) # Höhe des Displays
display = ScrollingTextDisplay(Nx, Ny)
display.display()

23
apps/wget.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
#pkill -9 wget
if [ ! -d /tmp/video ]; then
mkdir /tmp/video
fi
rm -f /tmp/video/*
if [[ ${3} =~ .*gif$ ]]; then
wget -O "/tmp/video/$(basename ${3})" "${3}" && ffmpeg -loglevel -8 -re -ignore_loop 0 -i "/tmp/video/$(basename ${3})" -pix_fmt rgb24 -vf "scale=(iw*sar)*min(${1}/(iw*sar)\,${2}/ih):ih*min(${1}/(iw*sar)\,${2}/ih), pad=${1}:${2}:(${1}-iw*min(${1}/iw\,${2}/ih))/2:(${2}-ih*min(${1}/iw\,${2}/ih))/2" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
elif [[ ${3} =~ .*(jpg|jpeg|png|bmp|tiff|webp)$ ]]; then
wget -q -O "/tmp/video/$(basename ${3})" "${3}";
ffmpeg -i "/tmp/video/$(basename ${3})" -pix_fmt rgb24 -vf "scale=${1}x${2}" -vcodec rawvideo -f image2pipe - | tee /tmp/video/matrix
while true; do
cat /tmp/video/matrix
sleep 1;
done
else
wget -O - -q "${3}" | tee "/tmp/video/$(basename ${3})" | ffmpeg -loglevel -8 -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
ffmpeg -loglevel -8 -f alsa default -stream_loop -1 -re -i "/tmp/video/$(basename ${3})" -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
fi

75
apps/wolfram.py Executable file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
import sys
import os
import numpy as np
import pygame
import time
Nx = int(sys.argv[1]) # Breite des Displays
Ny = int(sys.argv[2]) # Höhe des Displays
fps = 30
# Liste der Wolfram-Regeln, die angezeigt werden sollen
rules = [30, 90, 110, 60, 150, 126, 45, 105, 75, 214]
pause_duration = 5 # Dauer der Pause zwischen den Mustern in Sekunden
# Umwandlung der Regelnummer in eine Binärliste
def get_rule_bin(rule_number):
return np.array([int(x) for x in np.binary_repr(rule_number, width=8)])
# Regel für die nächste Generation
def next_gen(current_gen, rule_bin):
padded = np.pad(current_gen, (1,), mode='constant')
new_gen = np.zeros_like(current_gen)
for i in range(1, len(padded) - 1):
neighborhood = padded[i-1:i+2]
index = 7 - (neighborhood[0] * 4 + neighborhood[1] * 2 + neighborhood[2])
new_gen[i-1] = rule_bin[index]
return new_gen
# Funktion, um eine zufällige Farbe zu erzeugen
def random_color():
return np.random.randint(0, 256, size=3)
# Haupt-Loop für die Animation
clock = pygame.time.Clock()
while True:
for rule_number in rules:
rule_bin = get_rule_bin(rule_number)
# Initialisiere ein leeres Display (alle Zellen sind tot)
cells = np.zeros((Ny, Nx), dtype=np.uint8)
# Setze die mittlere Zelle der ersten Reihe auf 1 (Zelle ist lebendig)
cells[0, Nx // 2] = 1
# Erstelle einen Farbarray für die Zellen
color_map = np.zeros((Ny, Nx, 3), dtype=np.uint8)
# Animation Schleife
for row in range(1, Ny):
cells[row] = next_gen(cells[row - 1], rule_bin)
# Setze die Farben für die lebendigen Zellen
color_map[row] = np.where(cells[row][:, None], random_color(), 0) # Zufällige Farbe für lebende Zellen
# Erstelle das Bild aus den Zellen
image = np.zeros((Ny, Nx, 3), dtype=np.uint8)
for r in range(Ny):
for c in range(Nx):
if cells[r, c]:
image[r, c] = color_map[r, c]
# Schreibe das Bild in den Output-Stream
os.write(1, image.tobytes())
# Warte für die nächste Frame
clock.tick(fps)
# Pause von 5 Sekunden nach jedem Muster
time.sleep(pause_duration)

View File

@ -1,6 +1,7 @@
#!/bin/bash
export PATH="$PATH:/home/deckensteuerung/.config/bin"
youtube-dl -f 'worst[abr>=96]' --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
yt-dlp --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
#youtube-dl -f 'worst[abr>=96]' --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
#youtube-dl --no-progress --output - ${3} | ffmpeg -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
exit 0

View File

@ -5,7 +5,7 @@ ScreenX = 80
#height
ScreenY = 40
DefaultBrightness = 1.0
DefaultBrightness = 0.6
Serial = "/dev/ttyACM0"
@ -15,6 +15,7 @@ NoDataTimeout = 40
LogLevel = logging.DEBUG
UseGui = True
GuiFPS = 20
GuiScaleFactor = 15
WebHost = "0.0.0.0"
@ -22,25 +23,52 @@ WebPort = 8000
# first app is always running as default
Apps = [
{"guiname": "Lines", "name": "lines", "cmd": "apps/lines.py", "persistent": False},
{"name": "backlight", "cmd": "apps/backlight.py", "white": True},
{"guiname": "Lines", "name": "lines", "cmd": "./lines.py", "persistent": False, "path": "./apps/"},
{"guiname": "Wolfram", "name": "wolfram", "cmd": "./wolfram.py", "persistent": False, "path": "./apps/"},
{"guiname": "Digi Clock", "name": "digiclock", "cmd": "./digi_clock.py", "persistent": False, "path": "./apps/"},
{"guiname": "Text Scroller MQTT", "name": "textscroll", "cmd": "./textscroll.py", "persistent": False, "path": "./apps/"},
{"guiname": "Flicker", "name": "flicker", "cmd": "apps/flicker"},
{"guiname": "Pixelflut", "name": "pixelflut", "cmd": "apps/pixelflut", "persistent": True},
{"guiname": "Pong", "name": "pong", "cmd": "apps/pong.py"},
# {"guiname": "Pong", "name": "pong", "cmd": "apps/pong.py"},
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh", "persistent": False},
{"guiname": "Show Framebuffer", "name": "fbcp", "cmd": ["apps/fbcp", "/dev/fb0"]},
# {"guiname": "Show Framebuffer", "name": "fbcp", "cmd": ["apps/fbcp", "/dev/fb0"]},
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py"},
{"guiname": "Beispiel", "name": "example", "cmd": "apps/example.py"},
# {"guiname": "Beispiel", "name": "example", "cmd": "apps/example.py"},
# {"guiname": "Beispiel2", "name": "example2", "cmd": "apps/example2.py", "white": True},
{"guiname": "Fibonacci Clock", "name": "fibonacci-clock", "cmd": "apps/fibonacci-clock.py"},
{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
# juergen/pixelfoo
{"guiname": "Colored noise", "name": "cnoise", "cmd": "apps/cnoise", "persistent": False},
{"guiname": "Dual Moodlight", "name": "bimood", "cmd": "apps/bimood", "persistent": False},
{"guiname": "Maze", "name": "maze", "cmd": "apps/maze", "persistent": False},
{"guiname": "Dual Maze", "name": "dualmaze", "cmd": "apps/dualmaze", "persistent": False, "persistent": False},
{"guiname": "Predator & Prey", "name": "predprey", "cmd": "apps/predprey", "persistent": False},
{"guiname": "Congress noise", "name": "cnoise", "cmd": "pixelfoo/target/release/cnoise", "persistent": False},
{"guiname": "Game of Life", "name": "life", "cmd": "pixelfoo/target/release/life", "persistent": False},
{"guiname": "Matrix Code", "name": "matrix-code", "cmd": "pixelfoo/target/release/matrix-code", "persistent": False},
{"guiname": "Lorenz Attractor", "name": "lorenz", "cmd": "pixelfoo/target/release/lorenz", "persistent": False},
{"guiname": "Dual Moodlight", "name": "bimood", "cmd": "pixelfoo/target/release/bimood", "persistent": False},
{"guiname": "Maze", "name": "maze", "cmd": "pixelfoo/target/release/maze", "persistent": False},
{"guiname": "Dual Maze", "name": "dualmaze", "cmd": "pixelfoo/target/release/dualmaze", "persistent": False, "persistent": False},
{"guiname": "Predator & Prey", "name": "predprey", "cmd": "pixelfoo/target/release/predprey", "persistent": False},
# { "guiname": "Beat Saber Ceiling", "name": "beatsaberceiling", "cmd": "./beatsaberceiling.py", "path": "apps/beatsaberceiling" },
#{"guiname": "Fibonacci Clock", "name": "fibonacci-clock", "cmd": "apps/fibonacci-clock.py"},
# mathpixel
{"guiname": "Structure formation", "name": "swifthohenberg", "cmd": "apps/swifthohenberg.py", "persistent": False},
{"guiname": "Quadratisch", "name": "quadratic", "cmd": "apps/quadratic.py"},
{"guiname": "Pendel", "name": "pendulum", "cmd": "apps/pendlum.py"},
{"guiname": "Konvergenz", "name": "convergence", "cmd": "apps/convergence.py"},
{"guiname": "Sinic", "name": "sinic", "cmd": "apps/sinic.py"},
{"guiname": "Sinic 2", "name": "sinic2", "cmd": "apps/sinic2.py"},
# pixelthud
{"guiname": "Fading Pixels", "name": "fadingpxls", "cmd": "apps/fading_pixels.py", "persistent": False},
{"guiname": "Plane Wave", "name": "planewave", "cmd": "apps/plane_wave.py", "persistent": False},
{"guiname": "Rock-paper-scissors-spock-lizard", "name": "rps", "cmd": "apps/rps.py", "persistent": False},
{"guiname": "Doom Fire", "name": "doomfire", "cmd": "apps/doom_fire_psx2.py", "persistent": False},
# {"guiname": "Maxwell FDTD", "name": "fdtd", "cmd": "apps/fdtd.py", "persistent": False},
#{"guiname": "Stream", "name": "stream", "cmd": "apps/stream.sh", "persistent": False},
#{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
@ -53,14 +81,14 @@ Apps = [
]
# load additional apps from config/
import os
import os.path
configs = os.listdir("configs/")
import importlib
for config in configs:
file, ext = os.path.splitext(os.path.basename(config))
if ext != ".py" or file[0] == "_":
continue
modname = "configs."+file
lib = importlib.import_module(modname)
Apps += lib.Apps
# import os
# import os.path
# configs = os.listdir("configs/")
# import importlib
# for config in configs:
# file, ext = os.path.splitext(os.path.basename(config))
# if ext != ".py" or file[0] == "_":
# continue
# modname = "configs."+file
# lib = importlib.import_module(modname)
# Apps += lib.Apps

View File

BIN
filter/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -4,9 +4,10 @@
<title>Pixelserver Interface</title>
</head>
<body>
<h1>Andreas <i>DEBUG</i> Interface</h1>
<body style="display: grid; grid-template-columns: auto auto auto;">
<h1 style="grid-column: 1 / 4; text-align: center;">Andreas <i>production-ready</i> Interface</h1>
<div>
<h2>Kommando:</h2>
<form id='in' onSubmit="return request()">
<select id="list"></select>
@ -19,17 +20,49 @@
<input id="brightness" value=1.0 /><br/>
<button id="sendbrightness">Setzen</button>
</form>
</div>
<div style="border-left: solid 1px; padding-left: 10px;">
<h2>Gamma:</h2>
<form id='gammeform' onSubmit="return setgamma()">
Rot: <input id="gammar" value=2.8 /><br/>
Grün: <input id="gammag" value=2.65 /><br/>
Blau: <input id="gammab" value=2.65 /><br/>
Weiß: <input id="gammaw" value=2.65 /><br/>
<button id="sendgamma">Setzen</button>
</form>
<h2>Flip:</h2>
<form id="flipform" onSubmit="return setFlip()">
<input id="flipx" type="checkbox" /> Flip X <br/>
<input id="flipy" type="checkbox" /> Flip Y <br/>
<button id="sendflip">Setzen</button>
</form>
<h2>Filterimage:</h2>
<form id="filteform" onSubmit="return setFilter()">
<input id="filtername" value="test"></intput><br/>
<button id="filterflip">Setzten</button>
</form>
<h2>Filter expression:</h2>
<form id="filterexprform" onSubmit="return setFilterExpr()">
<input id="filterexpr" value="0.5+0.25*sin(x/3+t)"></intput><br/>
<button>Setzten</button>
</form>
</div>
<div style="border-left: solid 1px; padding-left: 10px; width: 600px;">
<h2>Crash Log:</h2>
<form id="crashlogform" onSubmit="return enableCrashLog()">
<button>start</button>
</form>
<textarea readonly id='crashlogs' style="width: 500px; height: 300px; display: none;"></textarea>
<h2>Log:</h2>
<textarea readonly id='logs' style="width: 500px; height: 500px;"></textarea>
<form id="logform" onSubmit="return enableLog()">
<button>start</button>
</form>
<textarea readonly id='logs' style="width: 500px; height: 300px; display: none;"></textarea>
</div>
<script>
function getRaw(from, callback){
var xhttp = new XMLHttpRequest();
@ -105,7 +138,8 @@
let r = document.getElementById('gammar').value;
let g = document.getElementById('gammag').value;
let b = document.getElementById('gammab').value;
let url = "/setgamma/" + r+"/"+g+"/"+b;
let w = document.getElementById('gammaw').value;
let url = "/setgamma/" + r+"/"+g+"/"+b+"/"+w;
getRaw(url, function test(){});
return false;
}
@ -117,13 +151,61 @@
return false;
}
function setFlip(){
let x = document.getElementById('flipx').checked;
let y = document.getElementById('flipy').checked;
console.log(x);
console.log(y);
getRaw("/filter/flipx/" + x, function test(){});
getRaw("/filter/flipy/" + y, function test(){});
return false;
}
function setFilter(){
let i = document.getElementById('filtername').value;
let url = "/filter/img/" + i;
getRaw(url, function test(){});
return false;
}
function setFilterExpr(){
let expr = document.getElementById('filterexpr').value;
let url = "/filter/expr/";
post(url, {"expr": expr});
return false;
}
function updateLog(){
getRaw("/apps/log", function(text){
document.getElementById('logs').innerText = text;
document.getElementById('logs').value = text;
});
}
updateLog();
setInterval(updateLog, 1000);
function updateCrashLog(){
getRaw("/apps/crashlog", function(text){
document.getElementById('crashlogs').value = text;
});
}
function enableCrashLog(){
document.getElementById("crashlogform").style.display = "none";
document.getElementById("crashlogs").style.display = "block";
updateCrashLog();
setInterval(updateCrashLog, 1000);
return false;
}
function enableLog(){
document.getElementById("logform").style.display = "none";
document.getElementById("logs").style.display = "block";
updateLog();
setInterval(updateLog, 1000);
return false;
}
getJSON("/apps/list", populateForm);

252
main.py
View File

@ -12,7 +12,10 @@ import time
import sys
import logging
import math
import numpy as np
import string
from collections import OrderedDict
import scipy.misc
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
running = True
@ -72,6 +75,16 @@ class LogReader(threading.Thread):
def stop(self):
self.running = False
class Frame:
def __init__(self, buffer, channels=3):
self.buffer = buffer
self.created = time.time()
self.channels = channels
def clone(self):
f = Frame(self.buffer+0, self.channels)
f.created = self.created
return f
########################################################################
# GUI #
########################################################################
@ -84,6 +97,7 @@ if config.UseGui:
self.cv = threading.Condition()
self.datasource = datasource.addListener(self.cv)
def run(self):
last_frame = time.time()
logging.info("Starting GUI")
sf = config.GuiScaleFactor
screen = pygame.display.set_mode((sf*config.ScreenX, sf*config.ScreenY))
@ -93,14 +107,26 @@ if config.UseGui:
pass
with self.cv:
self.cv.wait()
data = self.datasource.getData()
screen.fill((0, 255, 0))
for x in range(config.ScreenX):
for y in range(config.ScreenY):
i = x+y*config.ScreenX
color = (data[i*3+0], data[i*3+1], data[i*3+2])
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
frame = self.datasource.getData()
screen.fill((0, 0, 0))
if frame.channels == 3:
for x in range(config.ScreenX):
for y in range(config.ScreenY):
color = (frame.buffer[y, x, 0], frame.buffer[y, x, 1], frame.buffer[y, x, 2])
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
elif frame.channels == 4:
for x in range(config.ScreenX):
for y in range(config.ScreenY):
w = frame.buffer[y, x, 3]//2
color = (frame.buffer[y, x, 0]//2+w, frame.buffer[y, x, 1]//2+w, frame.buffer[y, x, 2]//2+w)
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
#logging.debug("Time to gui: "+str(time.time()-frame.created))
pygame.display.flip()
if time.time() < last_frame+1/config.GuiFPS:
time.sleep(time.time()-(last_frame+1/config.GuiFPS))
#time.sleep(0.01)
last_frame = time.time()
logging.info("Closing GUI")
def join(self):
with self.cv:
@ -128,19 +154,26 @@ class SerialWriter(threading.Thread):
logging.info("Serial Opened")
with self.cv:
self.cv.wait(timeout = 1/30)
data = self.datasource.getData()
frame = self.datasource.getData()
data = frame.buffer.reshape((config.ScreenX*config.ScreenY*frame.channels,)).astype(np.uint8).tobytes()
if self.updateGamma:
buf = bytearray(b"\x00")*3*256
buf = bytearray(b"\x00")*4*256
for i in range(256):
apply = lambda x, g: max(0, min(255, int(math.pow(x/255, g)*255)))
buf[i] = apply(i, self.r)
buf[i+256] = apply(i, self.g)
buf[i+512] = apply(i, self.b)
buf[i+512+256] = apply(i, self.w)
ser.write(b"\x02")
ser.write(buf)
self.updateGamma = False
ser.write(b"\01")
ser.write(data)
if frame.channels == 3:
ser.write(b"\01")
ser.write(data)
elif frame.channels == 4:
ser.write(b"\03")
ser.write(data)
logging.debug("Time to gui: "+str(time.time()-frame.created))
ser.flush()
except Exception as e:
if ser != None:
@ -154,9 +187,9 @@ class SerialWriter(threading.Thread):
with self.cv:
self.cv.notify_all()
super().join()
def setGamma(self, r, g, b):
def setGamma(self, r, g, b, w):
with self.cv:
self.r, self.g, self.b = r, g, b
self.r, self.g, self.b, self.w = r, g, b, w
self.updateGamma = True
self.cv.notify_all()
@ -164,31 +197,37 @@ class SerialWriter(threading.Thread):
# App #
########################################################################
class App(threading.Thread):
def __init__(self, cmd, param, listener, is_persistent):
def __init__(self, cmd, param, listener, is_persistent, is_white=False, path="."):
super().__init__()
#start app
if type(cmd) != list:
cmd = [cmd,]
args = cmd+[str(config.ScreenX), str(config.ScreenY), param]
self.app = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
self.app = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, cwd=path)
self.last_update = time.time()
self.cv = threading.Condition()
self.watchdog = WatchDog(lambda: self.isAppTimedOut(), lambda: self.terminateApp())
self.watchdog.start()
self.logreader = LogReader(self)
self.logreader.start()
self.datasource = DataSource(b"\x00"*config.ScreenX*config.ScreenY*3)
self.datasource = DataSource(Frame(np.zeros((config.ScreenY, config.ScreenX, 3))))
self.running = True
self.listener = listener
self.is_persistent = is_persistent
self.is_white = is_white
def run(self):
while running and self.running and self.alive():
oshandle = self.app.stdout.fileno()
try:
data = os.read(oshandle, config.ScreenX*config.ScreenY*3)
assert len(data) == config.ScreenX*config.ScreenY*3
bytes = 4 if self.is_white else 3
data = os.read(oshandle, config.ScreenX*config.ScreenY*bytes)
assert len(data) == config.ScreenX*config.ScreenY*bytes
buffer = np.frombuffer(data, dtype=np.uint8, count=config.ScreenX*config.ScreenY*bytes)
buffer = buffer.reshape((config.ScreenY, config.ScreenX, bytes))
frame = Frame(buffer, channels=bytes)
self.last_update = time.time()
self.datasource.pushData(data)
self.datasource.pushData(frame)
except Exception as e:
logging.debug("Exception in App.run")
with self.listener:
@ -197,6 +236,8 @@ class App(threading.Thread):
self.logreader.stop()
self.watchdog.join()
self.logreader.join()
self.app.wait()
logging.debug("App stopped")
def alive(self):
return self.app.poll() == None
def stop(self):
@ -206,6 +247,8 @@ class App(threading.Thread):
self.app.stderr.close()
self.watchdog.stop()
self.logreader.stop()
self.app.wait()
logging.debug("App stopped")
def getLog(self):
return self.logreader.getLog()
def terminateApp(self):
@ -220,16 +263,17 @@ class App(threading.Thread):
class AppRunner(threading.Thread):
def __init__(self):
super().__init__()
self.last_crashlog = ""
self.currentApp = -1
self.requestedApp = 0
self.intensity = config.DefaultBrightness
self.app = None
self.cv = threading.Condition()
self.param = ""
self.datasource = DataSource(b"\x00"*config.ScreenX*config.ScreenY*3)
self.datasource = DataSource(Frame(np.zeros((config.ScreenY, config.ScreenX, 3))))
self.serial = SerialWriter(self.datasource)
self.serial.start()
self.persistent_apps = {}
self.filters = OrderedDict()
#start persistent apps
for app, i in zip(config.Apps, range(len(config.Apps))):
if app["persistent"]:
@ -245,34 +289,41 @@ class AppRunner(threading.Thread):
logging.info("Requesting app: "+str(app))
def startApp(self, i, param=""):
app = config.Apps[i]
newapp = App(app["cmd"], param, self.cv, is_persistent=app["persistent"])
newapp = App(app["cmd"], param, self.cv, is_persistent=app["persistent"], is_white=app["white"], path=app["path"])
newapp.datasource.addListener(self.cv)
newapp.start()
if app["persistent"]:
self.persistent_apps[self.currentApp] = newapp
self.persistent_apps[self.currentApp] = newapp
return newapp
def updateApp(self):
if self.app != None and not self.app.is_persistent:
try:
if self.app != None and not self.app.is_persistent:
self.app.stop()
self.currentApp = self.requestedApp
if (self.currentApp in self.persistent_apps.keys()
and self.persistent_apps[self.currentApp].alive()):
self.app = self.persistent_apps[self.currentApp]
else:
self.currentApp = self.requestedApp
if (self.currentApp in self.persistent_apps.keys()
and self.persistent_apps[self.currentApp].alive()):
self.app = self.persistent_apps[self.currentApp]
else:
self.app = self.startApp(self.requestedApp, self.param)
except FileNotFoundError as e:
print(e)
def run(self):
logging.info("Starting Apprunner")
while running:
with self.cv:
if self.app == None or not self.app.alive():
if self.app != None:
self.last_crashlog = self.app.getLog()
self.requestedApp = 0
if self.requestedApp != None:
self.updateApp()
self.requestedApp = None
data = bytearray(self.app.datasource.getData())
for i in range(len(data)):
data[i] = int(data[i]*self.intensity)
self.datasource.pushData(data)
frame = self.app.datasource.getData().clone()
#logging.debug("Runner in time: "+str(time.time()-frame.created))
for _, f in self.filters.items():
frame.buffer = f(frame.buffer)
#logging.debug("Runner out time: "+str(time.time()-frame.created))
self.datasource.pushData(frame)
self.cv.wait()
self.serial.join()
if config.UseGui:
@ -282,8 +333,77 @@ class AppRunner(threading.Thread):
if self.app == None:
return ""
return self.app.getLog()
def setGamma(self, r, g, b):
self.serial.setGamma(r, g, b)
def setGamma(self, r, g, b, w):
self.serial.setGamma(r, g, b, w)
def setFilter(self, name, filter):
self.filters[name] = filter
def removeFilter(self, name):
if name in self.filters.keys():
del self.filters[name]
########################################################################
# Filter Api #
########################################################################
def MakeBrightnessFilter(intensity):
def filter(img):
return (img*intensity).astype(np.uint8)
return filter
def FlipXFilter(intensity):
return intensity[:,::-1,:]
def FlipYFilter(intensity):
return intensity[::-1,:,:]
def MakeBrightnessImageFilter(name):
img = scipy.misc.imread("filter/"+name+".png", flatten=True)/255
#img = np.transpose(img)
def filter(intensity):
intensity = intensity.astype(float)
for i in range(intensity.shape[2]):
intensity[:,:,i] *= img
return intensity.astype(np.uint8)
return filter
def strings(str):
allowed_chars = string.ascii_letters+string.digits+"+-*/()."
i = 0
outlist = []
while i != len(str):
if str[i] not in allowed_chars:
raise Exception("Unexpected char "+str[i])
if str[i] not in string.ascii_letters:
i+=1
continue
out = ""
while i != len(str) and str[i] in string.ascii_letters+string.digits:
out += str[i]
i+=1
outlist.append(out)
return outlist
def eval_safer(expr, x, y, t):
symbols = {"x": x, "y": y, "t": t,
"sin": np.sin, "cos": np.cos, "exp": np.exp, "tan": np.tan}
strs = strings(expr)
for s in strs:
if s not in symbols.keys():
raise Exception("unexpected symbol: "+s)
return eval(expr, {}, symbols)
def MakeBrightnessExprFilter(expr):
t0 = time.time()
x, y = np.meshgrid(np.arange(config.ScreenX), np.arange(config.ScreenY))
eval_safer(expr, 0, 0, 0)
def filter(intensity):
t = time.time()-t0
intensity = intensity.astype(float)
filter = 0*x+eval_safer(expr, x, y, t)
filter = np.clip(np.nan_to_num(filter), 0, 1)
for i in range(intensity.shape[2]):
intensity[:,:,i] *= filter
return intensity.astype(np.uint8)
return filter
########################################################################
# Web Api #
@ -336,6 +456,10 @@ def apps_start(name, param):
def apps_log():
return runner.getLog()
@route("/apps/crashlog")
def apps_log():
return runner.last_crashlog
@route("/apps/running")
def apps_running():
return config.Apps[runner.currentApp]["name"]
@ -344,22 +468,56 @@ def apps_running():
def index():
return bottle.static_file("index.html", root='html')
@route("/setgamma/<r>/<g>/<b>")
def setGamma(r, g, b):
@route("/setgamma/<r>/<g>/<b>/<w>")
def setGamma(r, g, b, w):
r = float(r)
g = float(g)
b = float(b)
runner.setGamma(r, g, b)
w = float(w)
runner.setGamma(r, g, b, w)
return "ok"
@route("/setbrightness/<i>")
def setGamma(i):
def setIntensity(i):
i = float(i)
if i < 0 or i > 1:
return "bad_value"
runner.intensity = i
runner.setFilter("0_intensity", MakeBrightnessFilter(i))
return "ok"
@route("/filter/flipx/<do>")
def flipx(do):
if do == "true":
runner.setFilter("1_flipx", FlipXFilter)
else:
runner.removeFilter("1_flipx")
return "ok"
@route("/filter/flipy/<do>")
def flipy(do):
if do == "true":
runner.setFilter("1_flipy", FlipYFilter)
else:
runner.removeFilter("1_flipy")
return "ok"
@route("/filter/img/<name>")
def setfilter(name):
if name == "none":
runner.removeFilter("3_imgfilter")
else:
runner.setFilter("3_imgfilter", MakeBrightnessImageFilter(name))
return "ok"
@post("/filter/expr/")
def filter_expr():
expr = request.forms.get('expr')
if expr == "" or expr == "none":
runner.removeFilter("5_brightnessfunction")
else:
runner.setFilter("5_brightnessfunction", MakeBrightnessExprFilter(expr))
return "ok"
########################################################################
# Startup #
########################################################################
@ -369,15 +527,21 @@ for app in config.Apps:
app["persistent"] = False
if "guiname" not in app.keys():
app["guiname"] = app["name"]
if "white" not in app.keys():
app["white"] = False
if "path" not in app.keys():
app["path"] = "."
cmd = app["cmd"]
#remove non existing apps
if type(cmd) == str and not os.path.isfile(cmd):
config.Apps.remove(app)
logging.warning("Removed app "+app["name"])
#if type(cmd) == str and not os.path.isfile(cmd):
#config.Apps.remove(app)
#logging.warning("Removed app "+app["name"])
runner = AppRunner()
runner.start()
#runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
run(host=config.WebHost, port=config.WebPort)
########################################################################

View File

@ -39,17 +39,17 @@ if hash cargo 2>/dev/null; then
cp -f external/pixelfoo/target/release/dualmaze apps/
fi
echo "Getting mathpixel"
mkdir -p external
pushd external
if [ ! -d "mathpixel" ]; then
git clone https://git.chaospott.de/andreas/mathpixel.git
fi
pushd mathpixel
git pull
bash setup.sh
popd
popd
#echo "Getting mathpixel"
#mkdir -p external
#pushd external
#if [ ! -d "mathpixel" ]; then
# git clone https://git.chaospott.de/andreas/mathpixel.git
#fi
#pushd mathpixel
#git pull
#bash setup.sh
#popd
#popd
echo "Getting pixelthud"
mkdir -p external