af24160a135b7eae5306f52c16987eb80b2827ca
[smartkram.git] / chiptan / flicker / flicker.sh
1 #!/bin/bash
2
3 # Flickergenerator
4 # Andreas Schiermeier <andreas@schiermeier.name>
5 # License: GNU Lesser General Public License v3
6
7 # Abbruch bei RC != 0 eines Befehls
8 set -e
9 # Fehlermeldung beim Zugriff auf nicht-initialisierte Variablen
10 set -u
11
12 LANG="C"
13
14 # Wer bin ich?
15 typeset    ME="${0##*/}"
16
17 # HIGH-Farbe (weiss auf schwarz)
18 typeset    HIGH="$(echo -e '\033[48;05;15m')"
19 # LOW-Farbe (schwarz auf schwarz)
20 typeset    LOW="$(echo -e '\033[48;05;0m')"
21 # Standardfarben
22 typeset    STD="$(echo -e '\033[0m')"
23
24 # Config, wenn vorhanden, einlesen
25 [ -r "${HOME}/.${ME}.cfg" ] && source "${HOME}/.${ME}.cfg"
26
27 # Breite eines Datenfeldes (ein Bit)
28 typeset -i FIELDW=${CFG_FIELDW:-3}
29 # Abstand zwischen Datenfeldern
30 typeset -i SPACEW=${CFG_SPACEW:-3}
31 # Höhe von Datenfeldern
32 typeset -i HEIGHT=${CFG_HEIGHT:-1}
33 # Flickerdaten
34 typeset    FLICKERDATA=""
35 # enthält clear-Steuersequenz wenn vor jedem Frame der Bildschrim geleert werden soll
36 typeset    CLEAR="${CFG_CLEAR-""}"
37 # Wartezeit zwischen Frames
38 typeset    WAIT="${CFG_WAIT:-"0.05"}"
39
40 # Initialisierungssequenz des Flickercodes
41 typeset    FLICKERSYNC="10000 00000 11111 01111 11111 01111 11111"
42
43 # enthalt später Flickerinitialisierung & -daten in Binärdarstellung
44 typeset    FLICKERSTREAM=""
45
46 # Standardfarben des Terminals
47 typeset    S=""
48 # HIGH-Sequenz n-Leerzeichen hell auf schwarzem Hintergrund
49 typeset    H=""
50 # HIGH-Sequenz n-Leerzeichen schwarz auf schwarzem Hintergrund
51 typeset    L=""
52
53 typeset    PREFIX=""
54 typeset    SUFFIX=""
55
56 # Sequenz als Nibbles vereinzelt und mit Taktsignal versehen
57 typeset    clknibbles=""
58
59 # Fertige Sequenz mit ANSI-Escapes
60 typeset    frames=""
61
62 # Laufzeitparametrisierung
63 typeset    nextctl=""
64
65
66 function usage {
67   echo
68   echo "${ME} -d Daten [-h Feldhoehe] [-w Feldbreite] [-s Abstand] [-t Wartezeit] [-c]"
69   echo
70   typeset optfmt='  -%s %-10s: %s\n'
71   printf "${optfmt}" "d" "Daten" "Übergabe der Flickerdaten"
72   printf "${optfmt}" "h" "Feldhoehe" "Feldhöhe in Zeilen"
73   printf "${optfmt}" "w" "Feldbreite" "Feldbreite in Zeichen"
74   printf "${optfmt}" "s" "Abstand" "Abstand in Zeichen zwischen Feldern"
75   printf "${optfmt}" "t" "Wartezeit" "Wartezeit zwischen Frames in Sekunden. \".\" als Dezimaltrenner."
76   printf "${optfmt}" "c" " " "Bildschirm löschen vor jeder Sequenz (\"bankenähnliche Ausgabe\")"
77   cat <<__EOF
78
79 Zur Laufzeit können mit den Buchstaben h, w, s, t, c gefolgt von + oder - die Parameter
80 beeinflusst werden. Mit q werden die aktuellen Parameter in ~/.${ME}.cfg gesichert.
81
82 __EOF
83 }
84
85 # erstellt Flickersequenz mit ANSI Escapesequenzen
86 function buildSeq {
87   S=$(printf "%s%${SPACEW}s" "${LOW}" " ")
88   H=$(printf "%s%${FIELDW}s%s" "${HIGH}" " " "${S}")
89   L=$(printf "%s%${FIELDW}s%s" "${LOW}" " " "${S}")
90
91   frames="${clknibbles}"
92
93   # in den Escape-Sequenzen von ${L} und ${H} können 0 und 1 vorkommen.
94   # Mit dem Tausch von 0 zu L und 1 zu H wird sichergestellt, dass nicht Teile der
95   # Escape-Sequenz ersetzt werden
96   frames="${frames//0/L}"
97   frames="${frames//1/H}"
98
99   # L & H in korrespondierende Escape-Sequenzen umsetzen
100   frames="${frames//L/${L}}"
101   frames="${frames//H/${H}}"
102
103   PREFIX="${LOW}|${S}"
104   SUFFIX="|${STD}"
105 }
106
107 # writeDefaults speichert aktuelle Flickering-Parameter (Breiten, Frequenz, etc.)
108 function writeConfig {
109   cat << "__EOF" > "${HOME}/.${ME}.cfg"
110 # Breite eines Datenfeldes (ein Bit)
111 CFG_FIELDW="${FIELDW}"
112 # Abstand zwischen Datenfeldern
113 CFG_SPACEW="${SPACEW}"
114 # Höhe von Datenfeldern
115 CFG_HEIGHT="${HEIGHT}"
116 # Flickerdaten
117 CFG_CLEAR="${CLEAR}"
118 # Wartezeit zwischen Frames
119 CFG_WAIT="${WAIT}"
120 __EOF
121 }
122
123 if [ ${#} -eq 0 ]
124 then
125   usage
126   exit 0
127 fi
128
129 while getopts "d:h:w:s:t:c" opt
130 do
131   case "${opt}" in
132     d) FLICKERDATA="${OPTARG}";;
133     h) HEIGHT="${OPTARG}";;
134     w) FIELDW="${OPTARG}";;
135     s) SPACEW="${OPTARG}";;
136     t) WAIT="${OPTARG}";;
137     c) CLEAR="$(clear)";;
138     *) usage >&2
139        echo "Fehler: Unbekannte Option \"${opt}\"" >&2
140        exit 1
141        ;;
142   esac
143 done
144 shift $((OPTIND - 1))
145
146 if [ ${#} -ne 0 ]
147 then
148   usage >&2
149   echo "Fehler: Unbekannte Option \"${@}\". Abbruch." >&2
150   exit 1
151 fi
152
153 if [ -z "${FLICKERDATA}" ]
154 then
155   echo "Fehler: keine Flickerdaten (-d) übergeben." >&2
156   exit
157 fi
158
159 # Eingabedaten bereinigen ("0x" von Zahlen entfernen, Leerzeichen entfernen) und neu ordnen; F004.. => 0F40...
160 FLICKERDATA="$(echo "${FLICKERDATA}" | \
161                   sed 's/0[xX]//g;
162                        s/ //g;
163                        s/\([0-9a-fA-F]\)\([0-9a-fA-F]\)/\2\1/g;'
164   )"
165
166 FLICKERSTREAM="${FLICKERSYNC} $(
167   echo -n "${FLICKERDATA}" | while read -n1 hex
168   do
169     i=0
170     printf " %04i" $(((0x${hex} & 2**(i) && 1 ) * 10**(3-i++) +
171                       (0x${hex} & 2**(i) && 1 ) * 10**(3-i++) +
172                       (0x${hex} & 2**(i) && 1 ) * 10**(3-i++) +
173                       (0x${hex} & 2**(i) && 1 ) * 10**(3-i++)
174                     ))
175   done
176   )"
177
178 # Nibbles ohne Takt-Bit in zwei Frames mit Takt umsetzen
179 for nibble in ${FLICKERSTREAM}
180 do
181   case "${nibble}" in
182     ????)
183       clknibbles="${clknibbles}${clknibbles:+|}1${nibble}|0${nibble}";;
184     *)
185       clknibbles="${clknibbles}${clknibbles:+|}${nibble}";;
186   esac
187 done
188
189 buildSeq
190
191 while true
192 do
193   IFS="|"
194   for frame in ${frames}
195   do
196     echo -n "${CLEAR}"
197     for (( i=0; i<HEIGHT; i++ )) {
198       echo "${PREFIX}${frame}${SUFFIX}"
199     }
200
201    # 2-in-1: 1. Timer (-t ${WAIT}) zwischen den Frames
202    #         2. Annahme von Befehlen
203    # wird kein Befehls-Zeichen gelesen, wird mit "continue" der verbleibende
204    # Schleifenkörper übersprungen und zum nächsten Frame übergegangen
205    read -s -N 1 -t ${WAIT} ctl || continue;
206    case "${ctl}" in
207      q) writeConfig
208         exit 0
209         ;;
210      h|w|s|t|c) nextctl="${ctl}";;
211      +) case "${nextctl}" in
212           h) let HEIGHT++;;
213           w) let FIELDW++;;
214           s) let SPACEW++;;
215           t) [ $WAIT != ".01" ] && WAIT=$(echo "${WAIT}-0.01" | bc -q);;
216           c) CLEAR="$(clear)";;
217         esac
218         # Flickersequenz mit den geänderten Parametern erstellen
219         buildSeq;
220         # Flickersequenz vom Anfang starten indem der aktuelle Durchlauf der 
221         # while- UND for-Schleife übersprungen wird
222         continue 2
223         ;;
224      -) case "${nextctl}" in
225           h) [ $HEIGHT -gt 1 ] && let HEIGHT--;;
226           w) [ $FIELDW -gt 1 ] && let FIELDW--;;
227           s) [ $SPACEW -gt 1 ] && let SPACEW--;;
228           t) WAIT=$(echo "${WAIT}+0.01" | bc -q);;
229           c) CLEAR="";;
230         esac
231         # Flickersequenz mit den geänderten Parametern erstellen
232         buildSeq
233         # Flickersequenz vom Anfang starten (siehe oben)
234         continue 2
235         ;;
236    esac
237   done
238 done