C++Guns – RoboBlog blogging the bot

18.05.2015

2qm2015 Gleisplan

Filed under: Allgemein — Tags: , — Thomas @ 22:05

Hier der aktualisierte Gleisplan. Immer wieder interessant, dass die Gleise in Wirklichkeit etwas anders liegen. Auf dem Plan sieht das so gequetscht aus.

Gleisplan

Gleisplan

Wir haben noch mehr Trennstellen und Kontaktgleise eingebaut um auch einen Blockbetrieb zu realisieren. So soll es möglich sein auf dem 2. Bahnhofsgleis zu rangieren, während auf dem ersten Gleis zwei Züge abwechselnd einfahren können.
Mal sehen ob das auch alles so klappt mit der Automatiksteuerung und dem mikro Relaiscomputer ;)

16.05.2015

integer overflow debugger trap

Filed under: Allgemein — Tags: , — Thomas @ 08:05

Benutzt man 16bit Integer statt 32bit um Speicher zu sparen und seine cache misses zu optimieren, läuft man Gefahr den Zahlenbereich von -32768 bis +32767 zu verlassen. So wie es für Floatingpoint Zahlen Überprüfungen auf over/underflow etc gibt, die ein Signal werfen bzw den Compiler anspringen lassen, so gibt es das auch für Integer. Ist wohl nur nicht so bekannt.

Für den GNU Compiler gibt es die Option -ftrapv [1]

This option generates traps for signed overflow on addition, subtraction, multiplication operations.

Leider wird nicht erwähnt, dass das Programm nun furchtbar langsamer läuft. Weiterhin scheinen sich noch im Geheimen Fehler zu verbergen die ich nicht ganz verstehe. [2]

When -ftrapv is in effect, libcalls are "necessary" so that the results of an operation can be propagated without making the call to the libgcc functions dead.

Im Prinzip funktioniert das Überprüfen auf Overflows aber ohne große Einbüßen wie dieser Artikel zeigt [3]

How much overhead should we expect from enabling integer overflow checks? Using a compiler flag or built-in intrinsics, we should be able to do the check with a conditional branch that branches based on the overflow flag that add and sub set. Assuming that branch is always correctly predicted (which should be the case for most code), the costs of the branch are the cost of executing that correctly predicted not-taken branch, the pollution the branch causes in the branch history table, and the cost of decoding the branch.
...
result in 3% penalty.
...
John Regehr, who’s done serious analysis on integer overflow checks estimates that the penalty should be about 5%, which is in the same ballpark as our napkin sketch estimate.

Der Artikel "Catching Integer Overflows in C" [4] gibt eine schöne Übersicht über die Techniken der Overflow detections und jener FAQ Eintrag "comp.lang.c FAQ list · Question 20.6b" [5] gibt eine Beispiel Implementierung. Mit dieser Einschränkung:

(Note: these functions all share one bug: they may fail if invoked on the largest negative integer, INT_MI

#include < stdio.h >
#include < limits.h >

int chkadd(int a, int b) {
	if(b < 0)
		return chksub(a, -b);
	if(INT_MAX - b < a) {
		fputs("int overflow\n", stderr);
		return INT_MAX;
	}
	return a + b;
}

int chksub(int a, int b) {
	if(b < 0)
		return chkadd(a, -b);
	if(INT_MIN + b > a) {
		fputs("int underflow\n", stderr);
		return INT_MIN;
	}
	return a - b;
}

int chkmul(int a, int b) {
	int sign = 1;
	if(a == 0 || b == 0) return 0;
	if(a < 0) { a = -a; sign = -sign; }
	if(b < 0) { b = -b; sign = -sign; }
	if(INT_MAX / b < a) {
		fputs("int overflow\n", stderr);
		return (sign > 0) ? INT_MAX : INT_MIN;
	}
	return sign * a * b;
}

Nun möchte man nicht für das komplette Programm die Überprüfung einschalten, sondern z.B. nur für 16bit Integer. Vielleicht implementiere ich einen eigenen Type mit überladenen Funktionen dafür. Irgendwann..

[1] https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35412
[3] http://danluu.com/integer-overflow/
[4] http://www.fefe.de/intof.html
[5] http://c-faq.com/misc/intovf.html

05.05.2015

Relais Flipflop

Filed under: Allgemein — Tags: , , — Thomas @ 22:05

Für das Gleisbildstellwerk brauchen wir Relais, da alles im 60er Jahre Style werden soll.
Ein Flipflop hat zwei gültige Zustände, von denen es hin und her floppt. Wir können ein Flopflop mit nur einem Relais aufbauen, wie es das erste Bild zeigt:

Flipflop1

Flipflop1

Einschalten funktioniert über S1, die Selbsthaltung über K1. Nur wie schaltet man aus? Man zieht einfach das Relais mit S2 gegen Masse. Dazu muss ein Widerstand als Spannungsteiler vor dem Relais eingebaut werden. Das hat mehrere Nachteile:
An dem Widerstand fällt die gesammte Versorgungsspannung an, er muss entsprechen groß realisiert werden und man hat entsprechende Verluste.
Die Spannung des Relais muss niedriger als die Versorgungsspannung sein.

Ein Flopflop mit zwei Relais ist wesentlich eleganter zu realisieren:

Flopflop2

Flopflop2

Eingeschaltet wird mit S1 / S2 und K1 / K2 dient der Selbsthaltung. Aus/Umgeschaltet wird mit dem Öffner Kontakt des jeweiligen anderem Relais. Das nennt man auch eine Schützverriegelung. Da immer nur ein Relais gleichzeitig Spannung haben kann.
Da die Taster ein Umschalten der Relais erzwingen, kann man auch nicht verhindern, dass beide Relais anziehen, wenn man beide Taster gleichzeitig drückt! Das ist natürlich schlecht, da ein Flioflop nur zwei gültige Zustände aus. Aus-Aus oder An-An gehört nicht dazu.
Die Lösung bringt ein weiteres Relais:

Flopflop3

Flopflop3

Die letzte Schaltung ist schon etwas komplizierter. Sie beseitigt nicht nur die angesprochenen Nachteile, sondern bietet zusätlich noch ein Power-On-Reset. Damit wird erreicht, dass das Flipflop gleich beim Einschalten sich in einem gültigen Zustand befindet. So muss es nicht erst nochmal nachträglich resettet werden.

Das Relais K1 hat keinen explizien Schließer für die Selbsthaltung. Es bekommt immer Spanunng über den Öffner von K2 und ist damit im Zweifel das dominante Relais. Beim Anlegen der Spannung zieht also K1 an, hält sich sozusagen selbst und öffnet den Stromkreis von K2.

Beim Betätigen von S2 zieht K2 an, öffnet den Stromkreis von K1 und hält sich schließlich selbst.
Jetzt kommt der erste Witz der Schaltung; Beim Betätigen von S1 zeiht K3 an und schließt den Stromkreis von K1. Gleizeitig öffnet es den Stromkreis von K2 und somit ist die Selbsthaltung von K1 gesichert.

Jetzt kommt der zweite Witz der Schaltung: Werden beide Taster S1 nd S2 gleichzeitig betätigt, gewinnt immer K1. Da K3 immer anzieht und so immer den Stromkreis von K2 unterbricht. Ganz egal was S2 sagt.

Wir haben also eine Schaltung mit 3Bit Speicherkapazität von denem wir nur 1 Bit nutzen. Dafür gewinnen wir eine saubere Umschaltung zwischen den Zuständen ohne die halbe Stromversorgung kurzzuschließen und wir überprüfen die Benutzereingabe. Das Flipflop ist also immer in einem zulässigen Zustand.

2qm 2015 Gleisbildstellwerk 2

Filed under: Allgemein — Tags: , — Thomas @ 11:05

Auf Ebay haben wir ein altes Gleisbildstellwerk ersteigert und ausgeschlachtet. Etwa die Hälfte der Größe müsste uns langen. Mir kam noch die Idee ein Ampere und Voltmeter für je die Haupt- und Bergstecke einzubauen.
So kann man schnell anliegende Spannungen und Ströme kontrollieren, fall die Lok sich mal nicht bewegen will.
Die Instrumente im Bild sind aber noch zu groß. Ich denke so 60x40mm wären besser.

Das Gleisbild selbst passt gut auf DIN A4, wahrscheinlich noch etwas kleiner. So bleibt Platz für weitere Schalten. Denn wir haben extremst viel Funktionalität auf nur 2qm!

Altes Stellpult

Altes Stellpult

Stellpult Testversion

Stellpult Testversion

04.05.2015

storing into constants (Bug of the day 4)

Filed under: Allgemein — Tags: — Thomas @ 17:05

From the Layhey Fortran 95 compiler user guide.

-[N]PCA
Protext Constant Arguments
Compile only. Default npca
Specify -pca to prevent invoked subprograms from storing into constants.

Example
call sub(5)
print *, 5
end
subroutine sub(i)
i = i + 1
end

This example would print 5 using -pca and 6 using -npca.

Greatest bullshit I've ever seen!
They create a compiler who can change compile time constants and make it the default behaviour.

27.04.2015

rsync cron ssh - daily compressed remote backup

Filed under: Allgemein — Tags: — Thomas @ 12:04

Add user
Add user on you lokal and remote host. Disable password auth on your remote host:
# adduser --disabled-password user

Create authentication keys on your lokal machine
$ ssh-keygen -t rsa

Copy user/.ssh/id_rsa.pub to the end of remote host user/.ssh/authorized_keys

rsync command
-h humand readable
-P show progress
-a archive mode
-v verbode
-z compress
-e using ssh

sync --stats -hPavze ssh /home/user user@remotehost:/home/user/daily

lokal cron command
Put this in an extra file in /etc/cron.d/
Calls rsync with username user daily at 10:01pm
The current date, standard and error output from rsync is redirected to cronlog file.

1 22 * * * user date >> /home/buchhaltung/cronlog; rsync --delete --stats -hPavze ssh /home/user user@remotehost:/home/user/daily >> /home/user/cronlog 2>&1

remote cron command
Put this in an extra file in /etc/cron.d/
Make a compressed backup every day at 11:01pm
1 23 * * * user tar -cvjf /home/user/dailybackup/daily_$(date +%Y%m%d).tar.bz2 /home/user/daily/

usefull links
http://www.marksanborn.net/howto/use-rsync-for-daily-weekly-and-full-monthly-backups/
http://www.marksanborn.net/linux/learning-cron-by-example/
http://www.linuxproblem.org/art_9.html

24.04.2015

return void func call (Pseudo Bug of the day 3)

Filed under: Allgemein — Tags: — Thomas @ 08:04

Heute gefunden. Im Prinzip geht der pseudobug so:

void calc(int a, int b) {
  std::cout << "func " << a + b << "\n";
}

void add(int a, int b) {
  return calc(a, b);
}

int main() {
  add(2, 3);
  return 0;
}

Na, wer sieht das Problem? Wird calc aufgerufen?

(Ja, aber explicit void zurückgeben is braindamage ;) )

21.04.2015

Passing random generators around (functor)

Filed under: Allgemein — Tags: — Thomas @ 02:04

Update: Version with template instead of std::function C++ Guns: Passing function objects around (Update Example random generators)

Consider the following problem: One has a parallelly callable function which needs a random generator for every thread. One wants to call the function several times and every time we want another random sequence. How can we implement this?

Between every function call the random generator must keep its states. So the caller of the function must own the generator objects and pass them to the function. The caller is also in responsible the initialize the random generators. One can feed them with random seeds. But i think there exist a better solutions [1]

We create an array of n random generators, initialize them with a random seed and pass them per pointer to the function.

Inside this function we need a distribution. A simple rand() % maxrand is not good. It uses only the lower bits which may not be random. With c++11 we can bind the random generator and number distribution together. This allows us the create a nice dice() object which represent our rand(). Of course, this object can be simply pass around.

Output:

Thread 0 sum 5021
Thread 0 sum 4966
Thread 3 sum 4996
Thread 3 sum 5014
Thread 1 sum 5007
Thread 2 sum 4970
Thread 1 sum 5092
Thread 2 sum 5002
Info sizeof std::function 16

Code:


// can be passed by value
int function2(std::function dice) {
  int sum = 0;
  for(int i=0; i < 1000; i++) {
    sum += dice();
  }
  return sum;
}

// pass by reference. it has state
int function(std::minstd_rand& generator) {
  // we need random numbers from 0 to 10
  std::uniform_int_distribution distribution(0,10);

  // we bind the generator and distribution together to a new functor dice()
  // std::bind copy its arguments by value. To change the given generator state,
  // my must pass it by reference. This can be easly done with std:ref.
  // Yeah C++ can be strange.
  std::function dice = std::bind ( distribution, std::ref(generator) );

  return function2(dice);
}

int main() {
  omp_set_num_threads(4);

  // create 4 random generatores with random seed
  std::minstd_rand seedGen;
  std::minstd_rand generators[omp_get_num_threads()];
  for(int i=0; i < omp_get_num_threads(); i++) {
    generators[i].seed(seedGen());
  }

#pragma omp parallel for
  for(int i=0; i < 8; i++) {
    const int id = omp_get_thread_num();
    // pass one generator to our function
    int sum = function(generators[id]);

    std::cout << "Thread " << id << " sum " << sum << "\n";
  }

  std::cout << "Info sizeof std::function " << sizeof(std::function) << "\n";

  return 0;
}

[1] Tina’s Random Number Generator Library
Tina’s Random Number Generator Library

Calling multiple random generator in parallel c++11

Filed under: Allgemein — Tags: — Thomas @ 00:04

Standard random functions are not thread safe. Calling them in parallel may result in undefined behavior or the threads blocking each other. This is a little test to create the same random sequences in serial as in parallel too.

Setting the same seed on a random generator as a start point should produce the same random sequence. First we create one generator and print the first three numbers. Restart it with different seed numbers.
Then we create three random generators with the same seeds and call them parallel. They should create the same sequences even if they are called interleaved.

Program output:

seriell
seed 1
48271
182605794
1291394886

seed 2
96542
365211588
435306125

seed 3
144813
547817382
1726701011

parallel
Thread 0 seed 1: 48271
Thread 0 seed 1: 182605794
Thread 0 seed 1: 1291394886
Thread 1 seed 2: 96542
Thread 1 seed 2: 365211588
Thread 1 seed 2: 435306125
Thread 2 seed 3: 144813
Thread 2 seed 3: 547817382
Thread 2 seed 3: 1726701011

Yep, work as expected :D
The program looks not as simple as it might can be. But I think it is okay.

void waitlong() {
  long a = 0;
  for(int i=0; i < 1000000; i++) {
      a += i;
  }
}

int main() {
  // create some random number and store them in expectedResult
  int expectedResult[3][3] = { 0 };
  std::minstd_rand generator;

  std::cout << "seriell\n";

  for(int id=0; id < 3; id++) {
    generator.seed(id+1);
    std::cout << "seed " << id+1 << "\n";
    for(int i=0; i < 3; i++) {
      expectedResult[id][i] = generator();
      std::cout << expectedResult[id][i] << "\n";
    }
  }

  // create three generators with the same seed and call them parallel
  std::cout << "\nparallel\n";

  std::minstd_rand generators[3];
  generators[0].seed(1);
  generators[1].seed(2);
  generators[2].seed(3);

  int result[3][3] = { 0 };
  int count = 0;

  // create 3 threads and interleave them with schedule(static, 1) num_threads(3)
  // We can not call cout in a threaded enviroment. So we must store the random numbers
  // in a array and print them laster. count and result is for this.
#pragma omp parallel for firstprivate(count) shared(result) schedule(static, 1) num_threads(3)
  for(int i=0; i < 3*3; i++) {
    int id = omp_get_thread_num();
    int rand = generators[id]();

    // not error free even with omp critical
//     std::cout << "Thread " << id << " seed " << id+1 << ": " << rand << "\n";

    result[id][count++] = rand;

    // wait a litte bit to give the next thread a chance to start
    waitlong();
  }

  // now print and compre the results
  for(int id=0; id < 3; id++) {
    for(int i=0; i < 3; i++) {
      std::cout << "Thread " << id << " seed " << id+1 << ": " << result[id][i] << "\n";
      if(result[id][i] != expectedResult[id][i]) {
        std::cout << "but expected " << expectedResult[id][i] << "\n";
      }
    }
  }

  std::cout << "\nValues per seed must be equal\n";
  return 0;
}

11.04.2015

Local variable will not retain values between function invocations (Bug of the day 2)

Filed under: Allgemein — Tags: — Thomas @ 07:04

Lokale Variablen behalten ihren Wert NICHT zwischen Funktionsaufrufe!

Es gibt in diesem Beispiel keinen Compilerfehler und auch keinen von Fortran erkannten Laufzeitfehler.
Es gibt Compiler Warnings die diesen Fehler zeigen, aber dazu bedarf es mehr Code. Ich habe dieses Beispiel möglichst klein und übersichtlich gehalten.

Ausgabe ohne Optimierung und ohne Fehler

 kater@ktux:~$ gfortran -Wall -Wextra -fcheck=all  bugoftheday_openreadclose.f95 
kater@ktux:~$ ./a.out 
first round
DEBUG address of fhdl         BFC4D8FC
 open. set fhdl to         100
 read
 all file

 second round
DEBUG address of fhdl         BFC4D8FC
 open. set fhdl to         100
DEBUG address of test         BFC4D90C
 set test variabel to        1000
 read
 all file

Man beachte die unterschiedlichen Adressen der lokalen Variablen. Daher wird der Fehler nicht getriggert.

Ausgabe mit Optimierung und mit Fehler

kater@ktux:~$ gfortran -Wall -Wextra -fcheck=all  bugoftheday_openreadclose.f95 -O1
kater@ktux:~$ ./a.out  
 first round
DEBUG address of fhdl         BFA7DE38
 open. set fhdl to         100
 read
 all file

 second round
DEBUG address of fhdl         BFA7DE38
 open. set fhdl to         100
DEBUG address of test         BFA7DE38
 set test variabel to        1000
 read
 ERROR expect fhdl to be         100 but is        1000

Diesmal sind die Adressen der lokalen Variablen gleich! Die Variable fhdl wird ungeplant ueberschrieben!!
Welche Adresse die lokalen Variablen bekommen und ob so der Fehler auftritt oder nicht, ist zufällig und nicht beeinflussbar!!!


! Local variable will not retain values between function invocations
! compile: gfortran -Wall -Wextra -fcheck=all  bugoftheday_openreadclose.f95
! turn on any optimization to trigger the error


subroutine openread(action)
  use testmodule
  implicit none
  character(len=*), intent(in) :: action
  integer :: fhdl

  if(action == 'open') then
    fhdl = 100
    write(*,'(A,Z16)') "DEBUG address of fhdl ", loc(fhdl)
    write(*,*) "open. set fhdl to", fhdl
  else if(action == 'read') then
    write(*,*) "read"
    if(fhdl /= 100) then
      write(*,*) "ERROR expect fhdl to be", 100, "but is", fhdl
    else
      write(*,*) "all file"
    endif
  else if(action == 'close') then
      write(*,*) "close fhdl", fhdl
  endif
end subroutine


subroutine overwriteLocalValue()
  implicit none
  integer :: test

  test = 1000
  write(*,'(A,Z16)') "DEBUG address of test ", loc(test)
  write(*,*) "set test variabel to", test
end subroutine

program bugoftheday
  use testmodule
  implicit none

  write(*,*) "first round"
  call openread("open")
  call openread("read")

  write(*,*)
  write(*,*) "second round"
  call openread("open")
  call overwriteLocalValue
  call openread("read")
end program
« Newer PostsOlder Posts »

Powered by WordPress