Source file: /~heha/basteln/PC/FunkUsb/dcf77-js.zip/dcf77-js.c

#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-80