22 Mar 2013

C shared objects exported to python (ctypes)

Some C Projects need a quick extension which is not performance relevant. Sometimes I wish to use python because it is available on all modern *nix systems. Also, programming in python is very efficient.

To solve this I have created a small example project which puts the functionality into a shared object (dll in windows-speak) which can be used from python.

The library:

lib.c
This library contains a simple function which appends the string "->aaa" to an existing string. This is an interesting example since it involves dynamic memory allocation and pointers.


#include <stdlib.h>
#include <string.h>
#include "lib.h"

//int main() {return 0;}

char *append(char* str) {
 //char *n = "";
 char *b = "->aaa";
 char *n = malloc(strlen(str) + strlen(b) + 1);
 n[0] = '\0';
 strcat(n, str);
 strcat(n, b);
 return n;
}


lib.h
char *append(char* str);


test.c
used for testing the the library from C.

#include <stdio.h>
#include "lib.h"

int main(int argc, char* argv[]) {
 printf("%s\n", append(argv[1]));
 return 0;
}

Compiling
#!/usr/bin/env bash
gcc -std=c99 -Wall -c -fPIC lib.c -o lib.o
gcc -std=c99 -Wall -shared -Wl,-soname,libt.so -o libt.so lib.o
gcc -std=c99 -Wall -o test test.c -L. -lt
rm lib.o


pylibt.py
Because python uses dynamic data types we need to give some hints about data types. Also pointers need to be declared.
#!/usr/bin/env python

from ctypes import *

cdll.LoadLibrary("./libt.so")
_libt = CDLL("libt.so")

#def initpylibt():
# pass

def append(s):
 _append = _libt.append
 _append.argtypes = [c_char_p]
 _append.restype = c_char_p
 return _append(s)


test.py
let's use the python module from a script.

#!/usr/bin/env python

import pylibt

ret = pylibt.append("0123456789as dfasdf asdf asdf asdf asf asdf")
print ret


7 Mar 2013

gcc - compile shared library

the Program to compile as shared library

string.h

/** string.h
 *
 * $Id$
 */

#ifndef STINRG_H_
#define STINRG_H_

#ifndef DEFAULT_BLOCKSIZE
 // num. bytes of junks of memory to allocate from the heap
 #define DEFAULT_BLOCKSIZE 128
#endif

#include <stdio.h>
#include <stdlib.h>

/** datatype to hold the character data */
struct string_t {
 int memsize;
 int length;
 char *text;
 int error;
};
typedef struct string_t string;

// function declarations
string str_redfile(FILE *file, int bs);
void str_dump(string *s);
unsigned long str_memsize(string *s);

#endif /* STINRG_H_ */

string.c

/** C example for reading file and allocating memory dynamically.
 *
 * Will read a file from disk while dynamically allocating memory.
 *
 * Example usage: ./exe [file-name]
 *
 * $Id$
 */

#include "string.h"

/** Read file from FILE pointer
 *
 * Reads data from FILE *file and dynamically allocates memory from the heap
 * to store the read data. If memory allocation fails, the returned string
 * struct's member error will equal to 1.
 *
 * If reading the file works, string.error will be 0.
 */
string str_redfile(FILE *file, int bs) {
 int charsize = sizeof(char);
 int charcount = 0;
 int blocksize;
 if (!bs)
  blocksize = DEFAULT_BLOCKSIZE;
 else
  blocksize = bs;
 int memsize = blocksize;
 char *line = (char*) malloc(charsize * (memsize+1));
 char *linep = line;

 string l;
 l.error = 0;
 l.length = 0;
 l.memsize = memsize;
 l.length = charcount;
 l.text = linep;

 while (1) {

  if(charcount > memsize) {
   // reallocate memmory
   memsize = memsize+blocksize;
   char *al = (char*) realloc(linep, charsize * (memsize+1));

   if (al == NULL) {
    fprintf(stderr, "Error, out of memory.\n");
    *line = '\0';
    l.text = linep;
    l.error = 1;
    return l;
   }
  }

  char c = fgetc(file);

  //if (c == '\n' || c == '\r' || c == EOF) {
  if (c == EOF) {
   charcount--;
   break;
  }

  // remember input char
  *line = c;
  *(line++);
  charcount++;
 }

 *line = '\0';
 l.text = linep;
 l.memsize = memsize;
 l.length = charcount;

 return l;
}

/** return allocated memory of a string struct
 */
unsigned long str_memsize(string *s) {
 return sizeof(string) + (s->memsize) + 1;
}

/** print struct string info
 */
void str_dump(string *s) {
 if (s->error == 0) {
  printf("memsize: %d\n", s->memsize);
  printf("length:  %d\n", s->length);
  printf("error:   %d\n", s->error);
  printf("text:    %.*s\n", 10, s->text);
  printf("total:   %lu\n", str_memsize(s));
 } else
  printf("Error: %d.\n", s->error);
}

test.c

/** test shared library
 *
 * an example how to use a shared library in a C program.
 *
 * $Id$
 */

#include "string.h"

/** main entry point
 */
int main(int argc, char **argv) {

 // we need 1 argument, the file name
 if(argc < 2) {
  fprintf(stderr, "Usage: %s [file]\n", argv[0]);
  exit(1);
 }

 // try to open the file
 FILE *f = fopen(argv[1], "r");
 if (f == NULL) {
  fprintf(stderr, "Error: failed to open file: %s\n", argv[1]);
  exit(2);
 }

 // allocate memory for storing the string and read contents
 string l = str_redfile(f, 0);

 // close file
 fclose(f);

 // check if reading worked
 if (l.error != 0) {
  fprintf(stderr, "Error: str_redfile() error: '%d'\n", l.error);
  fclose(f);
  exit(l.error);
 }

 // dump file infos
 str_dump(&l);

 // TODO: deallocate the memory
 // free memory
 //free(l.text);

 exit(EXIT_SUCCESS);
}

compiling the shared library and the test program from a bash:

#!/usr/bin/env bash
#
# gcc, compile a shared object file
#
# This example shows how to compile a shared object for C with gcc on linux. 
# required files: string.c, string.h test.
#
# $Id$

# configuration
bin=test
libname=string
v[0]=1; v[1]=0; v[2]=1;

# dynamic library
rm lib$libname* $bin
gcc -c -fPIC $libname.c -o $libname.o
gcc -shared -Wl,-soname,lib$libname.so -o lib$libname.so $libname.o
# create links for ld
#ln -s lib$libname.so lib$libname.so.${v[0]}
#ln -s lib$libname.so.${v[0]} lib$libname.so.${v[0]}.${v[1]}
#ln -s lib$libname.so.${v[0]}.${v[1]} lib$libname.so.${v[0]}.${v[1]}.${v[2]}

# compile executable
gcc $bin.c -o $bin -L. -l$libname

# run with proper LD_LIBRARY env variable, example:
# $ LD_LIBRARY_PATH=. ./test [path-to-file]

Running the executable. Make the newly compiled library available to ld through an environment variable.

$ LD_LIBRARY_PATH=. ./test [path-to-file]

6 Mar 2013

Reading file with dynamic memmory allocation in C

/*
 Will read a file from disk while dynamically allocating memory.

 Example usage: ./exe [file-name]
 */

#include <stdio.h>
#include <stdlib.h>

typedef struct string_t {
 int memsize;
 int length;
 char *text;
 int error;
};
typedef struct string_t string;

string redfile(FILE *file);
void str_dump(string *s);

string redfile(FILE *file) {
 int charsize = sizeof(char);
 int charcount = 0;
 int blocksize = 10;
 int memsize = blocksize;
 char *line = (char*) malloc(charsize * (memsize+1));
 char *linep = line;

 string l;
 l.error = 0;
 l.length = 0;
 l.memsize = memsize;
 l.length = charcount;
 l.text = linep;

 while (1) {

  if(charcount > memsize) {
   // reallocate memmory
   memsize = memsize+blocksize;
   char *al = realloc(linep, charsize * (memsize+1));

   if (al == NULL) {
    fprintf(stderr, "Error, out of memory.\n");
    *line = '\0';
    l.text = linep;
    l.error = 1;
    return l;
   }
  }

  char c = fgetc(file);

  //if (c == '\n' || c == '\r' || c == EOF) {
  if (c == EOF) {
   charcount--;
   break;
  }

  // remember input char
  *line = c;
  *(line++);
  charcount++;
 }

 *line = '\0';
 l.text = linep;
 l.memsize = memsize;
 l.length = charcount;

 return l;
}

void str_dump(string *s) {
 if (s->error == 0) {
  printf("memsize: %d\n", s->memsize);
  printf("length:  %d\n", s->length);
  printf("error:   %d\n", s->error);
  printf("text:    %s\n", s->text);
  printf("total:   %lu\n", sizeof(*s)+1);
 } else
  printf("Error: %d.\n", s->error);
}

int main(int argc, char **argv) {
 // string l = redfile(stdin);

 if(argc < 2) {
  fprintf(stderr, "Usage: %s [file]\n", argv[0]);
  exit(1);
 }

 FILE *f = fopen(argv[1], "r");
 if (f == NULL) {
  fprintf(stderr, "Error: failed to open file: %s\n", argv[1]);
  exit(2);
 }

 string l = redfile(f);

 if (l.error != 0) {
  fprintf(stderr, "Error: redfile() error: '%d'\n", l.error);
  fclose(f);
  exit(l.error);
 }

 str_dump(&l);

 fclose(f);

 exit(0);
}

5 Mar 2013

3sat shooting



We did some test flights today for the German TV 3sat. The shots will be aired in March this year. Just a couple of days before the shooting we got our very own FLIR PS thermal camera, yay.

Some footage of the shooting:


24 Feb 2013

Example using getopt from C


// $Id$

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

struct opt_t {
 char* opt;
 char* arg;
};

int opt_parse(int argc, char *argv[], const char * optstring) {

 //Specifying the expected options
 //The two options l and b expect numbers as argument
 while ((option = getopt(argc, argv, optstring /*"apl:b:"*/)) != -1) {
  switch (option) {
   case 'd':
    printf("Database: %s\n", optarg);
    break;
   default: 
     printf("Huh?"); 
    return EXIT_FAILURE;
  }
 }
 
 return 0;
}

int main(int argc, char** argv) {
 const char* opts = "d:";
 
 int r = opt_parse(argc, argv, opts);
 
 return r;
}

13 Feb 2013

Problems programming ATtiny85 from Arduino IDE

After several successful firmware uploads from the Arduino IDE via Arduino Uno as ISP programmer to an ATtiny85 it suddenly stopped to work.

I have not figured out why this happens, a friend of mine has the same problem and couldn't find what caused it neither.

Long story short, the work around is to use Arduino IDE to compile the sketch and avrdude directly to do the uploading.

Compiling

Compiling code for ATtiny85/45 in Arduino IDE requires that the hardware definitions of these chips are installed in Arduino IDE:

Once the code is compiled it can be found in /tmp/build[0-9]*.tmp/*.hex on a *nix platform (C:\Windows\temp\... probably on windows, check the Arduino IDE manual).

Once the location of the hex file is known, I copy it to a safe place (data in the temp folder are purged occasionally).

Arduino as ISP

I am using Arduino Uno as programmer. For this wot work the arduino must be flashed with an already available sketch called: ArduinoISP (found under Examples).

Once this sketch is uploaded, the Arduino Uno works as In System Programmer and can be hooked up to your ATtiny device. The Arduino Uno is the used as "bridge" between your USB port on the computer and the tiny, as well as power supply.


Uploading

Using avrdude with the following parameters worked out of the box for me on a virgin ATTiny85 from linux:
$ avrdude -P /dev/ttyACM0 -p t85 -c avrisp -b 19200 \
          -v -F -U flash:w:pulse_in.cpp.hex



4 Feb 2013

nag-o-meter



This is our new status display of the Service Monitoring System I have built up during the last years. My cow-orkers keep forgetting to act upon warnings. I thought I could add some extra motivation for fixing errors in a timely manner (note the nice sound it makes :) ).

A little bit of python, a little bit of *nix voodoo glued together with a bash script - done.



APM log analysis



I personally prefer to use mavproxy and it's example utilities (such as mavgraph.py). It should be ported to windows by now. More info about mavproxy on diydrones.com.

Randy Mackay made some nice instructional videos on analyzing APM log files with Mission Planner and Microsoft Excel (basically any spreadsheet tool will do as long as it understands delimited text files).

First, the onboard logs are always available except if the APM is destroyed, for example, in a crash. This video shows how to analyse the onboard logs of an ArduPilot Mega board:


For those new to APM, Randy explains in more detail how to obtain the onboard logs of an APM 2.5. Make sure to connect your APM board via USB to the computer and you might use APM Mission planner to download the onboard logs:


Telemetry log files (*.tlog) provide much more information than onboard logs. The telemetry logs can be analysed in Mission Planner as well. Here a short overview on ho to work with tlogs in mission planner:


31 Jan 2013

2012 WWW Fuller Symposium, Washington DC



Dr. Lian Pin Koh from ETH Zürich speaks about the application of drones (small, autonomous Aircrafts) for biologic conservation:


A ResearchDrone's MAJA is featured at 16:31.

28 Jan 2013

/dev/null

i just figured where all the stuff that is piped to /dev/null goes to.


16 Jan 2013

APM Camara control print

The first prototype, lets put this into a drone.


15 Jan 2013

APM Camera power control & CHDK intervalometer

Finally I made some progress on the r/c camera control. A fairly simple and cheap solution is to solder cables to the power button of a cannon camera. Hook these up to an ATtiny85 (45 would probably do as well). The ATtiny reads a PPM signal from an r/c receiver and switches the camera on or off accordingly.

ATtiny  pin layout:


Schema:


Demo:





ATTiny Code (C++ Arduino):

/**
 * Read PPM signal
 * 
 * Decode r/c receiver PPM servo signal and turn lights on 
 * according to stick position.
 *
 * $Id$
 */

// pin setup
#define PIN_PPM  0
#define PIN_POS1 4

unsigned long duration, lastgood = 0;
int position = 0;

void setup() {
  pinMode(PIN_PPM, INPUT);
  pinMode(PIN_POS1, OUTPUT);
  digitalWrite(PIN_POS1, LOW);
}

void loop() {
  // the length of the pulse (in microseconds) or 0 if no pulse 
  // started before the timeout (unsigned long)
  duration = pulseIn(PIN_PPM, HIGH, 20000); 
  if (duration == 0)
    duration = lastgood;
  else
    lastgood = duration;
  
  position = map(lastgood, 1000, 2000, 0, 1);
  
  if (position > 0)
    digitalWrite(PIN_POS1, HIGH);
  else
    digitalWrite(PIN_POS1, LOW);
}



Intervalometer Code (lua):

--[[
rem 2013-01-13 by Simon Wunderlin, ResearchDrones LLC
@title ResearchDrones Fast Interval
@param a = interval (sec/10)
@default a 10
--]]

function camera_close()
 click "display"
 sleep(1000)
 click "display"
 sleep(1000)
 shut_down()
 sleep(5000)
end

--[[
f = get_usb_power(1)
if f == 1 then camera_close() end 
]]--
--shoot()

repeat
 start = get_tick_count()
 
 press("shoot_half")
 repeat
  sleep(50)
  until get_shooting() == true
 click("shoot_full")
 release("shoot_half")
 
 sleep(a*100 - (get_tick_count() - start))
until ( false )

12 Jan 2013

ResearchDrones Deployments



View Deployments in a larger map

ResearchDrones in Congo

In December 2012 Remo and I were invited to do a technology demonstration in the National Park Odzala, in the Republic of Congo (not DRC).

It was an interesting field trip, the first time for us being out there with researchers.

Out of this 9 day trip, only 2 days could be spent on flying, testing and training. The rest of the time we spent on airplanes, hotels and most of it in a Jeep driving roughly 1600km from the airport to the national park on more or less something we would call roads.


View Odzala National Park, Congo in a larger map

The 3 nights in the field we spent in a camp of the Forrestry Administration in Mbomo (1.5h away from our flying site).

Here a short video of the test flights we were conducting in the Savannah in the middle of the national park.

5 Jan 2013

ResearchDrones test flights with thermal imaging cameras

On the 3rd January we have been conducting test flights with 2 different thermal imaging cameras in Switzerland.

We have been using an FLIR (forward looking) and a NEC F30 (downward looking) on the current UAV platform connected to a long range video transmission system.

The FLIR HS-324 produced quiet good results




At 0:30 and 1:38 there can people and cars be seen standing on the runway. This footage was taken from around 100m above the ground.

Some cars and people standing on the runway can be seen at 0:50, 1:12 and 1:30.