#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/joystick.h>
#include <string.h>
#include <time.h>
#include <stdbool.h>
#include <signal.h>
#include <stdlib.h>
static bool inverted;
static int histo[50];
static int weight; // +1 für >500, -1 für -500
static void addHisto(int ms) {
int bucket=ms/20;
if (bucket<0) bucket=0;
if (bucket>49) bucket=49;
histo[bucket]++;
if (ms>500) {
weight++;
if (weight>10) {
weight=0;
memset(histo,0,sizeof histo); // Histogrammdaten löschen
inverted=!inverted;
printf(" Dateninversion!\n");
}
}else if (weight) --weight;
}
static void outHisto(void) {
int i,max=0;
for (i=0; i<50; i++) if (max<histo[i]) max=histo[i];
char s[32];
int n=snprintf(s,sizeof s,"%d",max); // erforderliche Anzahl Stellen
for (i=0; i<50; i++) if (histo[i]) {
printf("%3d %*d ",i*20+10,n,histo[i]);
int j,k=((74-n)*histo[i]+(max>>1))/max;
for (j=0;j<k;j++) putchar('#');
putchar('\n');
}
}
static void sighandler(int sig) {
if (sig!=SIGINT) return;
putchar('\n');
outHisto();
exit(0);
}
static int getbcd(int bcd, int bits) {
bcd&=(1<<bits)-1;
if ((bcd&0x0F)>=10) return 255;
return (bcd>>4)*10+(bcd&0x0F);
}
static time_t prevtime;
// "Decode" DCF77 data and show values
// For simplicity, no parity or data check here
static void showtime(int64_t data) {
static const char* const dow[]={
"Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Sonnabend"};
struct tm t;
int tz_hour;
memset(&t,0,sizeof t);
tz_hour=3-getbcd((int)(data>>17),2); // +1 oder +2
t.tm_min =getbcd((int)(data>>21),7);
t.tm_hour=getbcd((int)(data>>29),6);
t.tm_mday=getbcd((int)(data>>36),6);
t.tm_wday=getbcd((int)(data>>42),3)%7;
t.tm_mon =getbcd((int)(data>>45),5)-1;
t.tm_year=getbcd((int)(data>>50),8)+100;
t.tm_isdst=tz_hour>=2;
printf("ISO 8601: \e[36m%4d\e[0m-\e[35m%02d\e[0m-\e[33m%02d\e[0m"
"T\e[32m%02d\e[0m:\e[31m%02d\e[0m:%02d"
"\e[1m+%02d\e[0m:%02d (\e[34m%s\e[0m)\n",
t.tm_year+1900,
t.tm_mon+1,
t.tm_mday,
t.tm_hour,
t.tm_min,
t.tm_sec,
tz_hour,
0,
dow[t.tm_wday]);
if (__builtin_parity((int)(data>>21)&0xFF)
|| __builtin_parity((int)(data>>29)&0x7F)
|| __builtin_parity((int)(data>>36)&0x7FFFFF)) {
printf("Paritätsfehler!\n");
return;
}
if (data&1
|| !((int)(data>>20)&1)
|| (tz_hour!=1 && tz_hour!=2)
|| t.tm_min>=60
|| t.tm_hour>=24
|| (unsigned)(t.tm_mday-1)>=31
|| t.tm_mon>=12
|| t.tm_year>=200) {
printf("Datenfehler!\n");
return;
}
time_t time=mktime(&t);
if (time-prevtime==60) {
printf("Stimmt so: %s",asctime(&t)); // die Unix-Form (englisch)
if (!getuid()) { // wenn Root
char s[128];
snprintf(s,sizeof s,"date -s \"%s\"",asctime(&t));
// das \n des asctime() scheint dem date-Kommando nichts auszumachen
if (system(s)) { // Uhr stellen
printf("Fehler beim Stellen der System-Uhr!");
}else{
printf("Das Stellen der System-Uhr scheint geklappt zu haben.");
}
sighandler(SIGINT);
}
}
prevtime=time;
}
// Bit-Index und Länge für 7 Felder, siehe oben (nur 6 Unbuntfarben sind sicher)
static const char ary_idx[]={21,7,29,6,36,6,42,3,45,5,50,8,1,14};
// Textfarben anhand Bitnummer setzen. Wetterdaten absichtlich kontrastarm.
// Unterstreichungen bei korrekten Start- und Paritätsbits ausgeben
static void hilite(int sec,int64_t datagram) {
int i;
for (i=0;i<sizeof ary_idx; i+=2) {
if ((unsigned)(sec-ary_idx[i])<ary_idx[i+1]) {
printf("\e[%dm",(i>>1)+31); // set color attribute
break;
}
}
switch (sec) { // datagram muss nicht maskiert werden, die höheren Bits sind noch 0
case 0: if (datagram) return; break;
case 17:
case 18: printf("\e[1m"); return; // fett
case 20: if (!(datagram>>20)) return; break;
case 28: if (__builtin_parity((int)(datagram>>21))) return; break;
case 35: if (__builtin_parity((int)(datagram>>29))) return; break;
case 58: if (__builtin_parity((int)(datagram>>36))) return; break;
default: return;
}
printf("\e[4m"); // Unterstreichen wenn OK
}
// Das einzig mögliche Kommandozeilenargument ist ein Pfad zum Joystick.
// Sonst /dev/input/js0, der erste oder einzige Joystick.
// Als root gestartet setzt das Programm die Uhrzeit.
// (Wäre ja sonst auf einem Raspberry wenig sinnvoll.)
int main(int argc, char**argv) {
char*jsname=argv[1];
if (!jsname) jsname="/dev/input/js0";
int dcf77button=0;
int fd=open(jsname,O_RDONLY);
if (fd<0) {
perror(jsname);
return fd;
}
// info output
char number_of_axes,number_of_buttons;
int driver_version;
char id_string[128];
ioctl(fd,JSIOCGAXES,&number_of_axes);
ioctl(fd,JSIOCGBUTTONS,&number_of_buttons);
ioctl(fd,JSIOCGVERSION,&driver_version);
ioctl(fd,JSIOCGNAME(sizeof id_string),id_string);
printf("Joystick %s Eigenschaften: Achsen=%d, Knöpfe=%d, Version=0x%X\nName=\"%s\"\n",
jsname,number_of_axes,number_of_buttons,driver_version,id_string);
if (number_of_buttons<dcf77button+1) {
fprintf(stderr,"Der fragliche Joystick muss mindestens 1 Knopf haben!\n");
return -42;
}
// Status auslesen (zyklisch)
signal(SIGINT,sighandler);
bool shortpulse=false;
int second=-1; // Sekunde (Null nach Minutenlücke)
int64_t datagram;
int32_t t_buttonpress; // Zeit des Knopfdrucks
bool lastbuttonstate=-1; // unbekannt
for(;;) {
struct js_event e;
int r;
r=read(fd,&e,sizeof e);
if (r>0) {
if (e.type==JS_EVENT_BUTTON
&& e.number==dcf77button) { // Nur Knopf 0 interessiert, Achsen sind eh' wertlos
if (e.value^inverted) { // Knopf gedrückt = Beginn der Trägerabsenkung?
if (shortpulse) { // Diese steigende Flanke ignorieren
shortpulse=false;
}else if (lastbuttonstate==false) {
int diff=e.time-t_buttonpress; // sollte 1000 oder 2000 (ms) sein
if (diff>1500) {
printf("\n"); // Bit-Zeile abschließen
if (diff<2500) {
if (second==59) {
showtime(datagram);
}
second=0; // Sekundenzähler rücksetzen
}else second=-1; // Bits nicht mitschneiden, aber anzeigen
datagram=0;
}
printf("?"); // Anzeige Bit-Start
}
fflush(stdout);
t_buttonpress=e.time; // Zeitpunkt der Vorderflanke merken
lastbuttonstate=true; // ab sofort steht der Zeitstempel zur Verfügung
}else if (lastbuttonstate==true) { // Ende der Trägerabsenkung
int puls=e.time-t_buttonpress; // sollte 100 oder 200 (ms) sein
addHisto(puls);
if (puls<50) {
shortpulse=true; // Prellen ignorieren
printf("\bS"); // Anzeige (Fragezeichen überschreiben)
}else{
char bit=puls>150?'1':'0';
if (puls>250) bit='F'; // Fehler-Bit
if (second>=0) {
if (second<64 && bit!='F') datagram|=(int64_t)(bit&1)<<second;
hilite(second,datagram);
second++;
}
printf("\b%c\e[0m",bit); // Fragezeichen oder 'S' überschreiben
}
fflush(stdout);
lastbuttonstate=false;
}
}
}else{
perror(jsname);
break;
}
}
close(fd);
putchar('\n');
outHisto();
return 0;
}
Detected encoding: UTF-8 | 0
|