Sunday, January 25, 2015

Curl C to google sheets

A quick post about using the CURL C API to upload data to google spreadsheets. I used it upload temperature and humidity data that I am tracking at home. There are a few steps

1.) create a google spreadsheet the normal way (with headings for each data item you want to save)

2.) get auth from google:
                curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.com/accounts/ClientLogin");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "Email=YOUREMAIL&Passwd=YOURPASSWORD&accountType=GOOGLE&source=cURL&service=writely");

3.) Form your data in the correct XML format:
      sprintf(postData, "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'> <gsx:date>%s</gsx:date>  <gsx:time>%s</gsx:time>  <gsx:temp>%f</gsx:temp>  <gsx:relhum>%f</gsx:relhum><gsx:location>%d</gsx:location><gsx:device>oregon</gsx:device></entry>", 
dateStr, timeStr, s->getTemperature(), s->getHumidity(), s->getChannel());

4.) Post the data with the correct fields to the spreadsheet:
                        curl_easy_setopt(curl, CURLOPT_URL, "https://spreadsheets.google.com/feeds/list/YOURSPREADSHEETKEY/1/private/full");
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(postData));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData);
curl_easy_setopt(curl, CURLOPT_POST, true);

struct curl_slist *headerlist=NULL;
char authHeader[600];
sprintf(authHeader, "Authorization: GoogleLogin auth=%s", g_auth);
cout << "auth header " << authHeader << endl << flush;
headerlist = curl_slist_append(headerlist, authHeader);
headerlist = curl_slist_append(headerlist, "GData-Version: 3.0");
headerlist = curl_slist_append(headerlist, "Content-Type: application/atom+xml");


Full function code at https://github.com/arcanon/curlgooglespreadsheet/blob/master/googledocs-c.cpp

Monday, February 24, 2014

Modifying coova chilli to allow anonymous users for a private hotspot using automatic MAC addresses

So I spent a lot of time looking for a way to allow anonymous users to a coova chilli hotspot. Anonymous in the sense that a user can just click a button and get 30 min Internet access without having to type a password and what not. It made complete sense to use the MAC address as the users ID, but this only worked if you entered the MAC already. And NO where did I find that you could accept any new MAC address as a new user. Eventually I learnt enough about coova chilli to do this myself. The trick is to change the function dologin in the haserl script /etc/chilli/www/config-local.sh (use your password instead of XXXX) to


dologin() {
    res="$(echo "select username from radcheck where username ='$REMOTE_MAC';" | mysql -u root -pXXXX radius)"
    if [ "$res" = "" ]
    then
        echo "insert into radcheck (username, attribute, op, value) values ('$REMOTE_MAC', 'Cleartext-Password', ':=', 'password');" | mysql -u root -pXXXX radius
        echo "insert into radusergroup (username, groupname) values ('$REMOTE_MAC', '30min');" | mysql -u root -pXXXX radius
    fi
#    url=$(chi_login_url "$FORM_username" "$FORM_password" "$FORM_userurl")
    url=$(chi_login_url "$REMOTE_MAC" "password" "$FORM_userurl")
    cat <<ENDHTML
<html><head>
<meta http-equiv="refresh" content="0;url=$url"/>
</head></html>
ENDHTML
    wisprLoginResultsURL "$url" 
}


Once you have done this, every time a new device logs into your web page, the script will check if the MAC address exists or not. If not it will require any username. I changed my login page to just be a button and made the other fields hidden, like so edit /etc/chilli/www/login_form.tmpl :

<!--
  --  The login form
  -->

<div id="login-form">
<table>
<tr>
<!--  <td>Usernamebb</td> -->
  <td><INPUT NAME="username" VALUE="user1" TYPE="hidden"></td>
</tr>
<tr>
<!--  <td>Password</td> -->
  <td><INPUT NAME="password" VALUE="password" TYPE="hidden" TYPE="password"></td>
</tr>
<tr>
  <td colspan="2" nowrap align="center">
    <input type="submit" name="button" value="Login & Accept Terms">
  </td>
</tr>
</table>
</div>



Wednesday, February 12, 2014

Hacking a hermes pro router to get a isdn line working

So I had an issue that a Hermes Pro/S+ router died. This is used by pharmacy software (more specifically Apotheken Software) to get order supplies from the supplier. Now of course when we asked the support company for some help, they immediately said that would be 1000 euros please. In fact they did not want to tell us the price. They just wanted to charge directly. And they did not offer any other options.

So instead i ordered a Hermes Pro/SH online from ebay. So it arrived. Cool! Then i found out that it did not have an IP written down. For anyone that may know, that means its not really easily configurable. The next way to do this was over the V24 port. This is a serial port and should connect easily to your PCs COM port. That's if u still have one:). Basically a Ethernet to 9 pin serial port cable is necessary. Now looking online, they did exist, but that meant ordering a cable and we did not really have the time for that. So a i decided to make my own. Just cut open a Ethernet cable and join it to 9 pin serial cable (USB adapter which i bought for my laptop).

So i did this following the standard that exists for Ethernet to 9 pin serial. Then i fired up putty to telnet into the router, but nothing:( my heart sank. So much time and still no solution. And the business needs this to run properly...  so i head home a bit unhappy. On the way i remember one point of data i got from the sales guy. That was that the Hermes router was built from an old Cisco router. So i start googling a Cisco V24 port router. I find a page detailing the mapping for Cisco. And what do u know, its not the same as the standard that i tried before! So i do some rewiring and fire up putty again. I have never been so happy to see a login screen! This was the result:

Wednesday, January 1, 2014

Better Stereo (?) using Rectified Left and Right images and stereo Q matrix with and without zero disparity

Left and right images with zero disparity calibration and the resulting disparity map. With higher possible disparity values (this is a paremeter for the alogithm), the left of the map is black, but gives better results in general for the found disparity.
So the stereo camera is important to provide 3D information of the world that is currently visible. The difference in position of an object in the left and right images defines the objects position. With this information we can start reconstructing the world around the camera. So there are two points:
  1.) Determining the relative position of objects in the left and images in terms of how many pixels the object's position is different from left to right. This is done using something like stereo block matching.
  2.) Translate this pixel difference in to real world values like centimeters and meters using the camera parameters.

Before 1 and 2 can be done, the left and right images need to be rectified to fit into the normalized camera reference point, so the algorithms can correctly run. See references on epipolar lines or rectified stereo images.

The one issue I ran into was how was the Q matrix in 2 above defined. ScaViSLAM uses a Q matrix with "zero disparity" this means that the Q matrix looks like this (see StereoCamera::Q()):
  1    0    0    Cx
  0    1    0    Cy
  0    0    0     f
  0    0    1/b  0

where (Cx, Cy) is the camera center point, f is the camera focal distance and b is the baseline distance (distance between the left and right camera center points). To obtain this value using openCV function cvStereoRectify, you must use the CV_CALIB_ZERO_DISPARITY flag. If you don't use this then your matrix looks like:

  1    0    0    Cx
  0    1    0    Cy
  0    0    0     f
  0    0    1/b  b'/b

Where b' is the shift between the 2 cameras. Using a non zero disparity should help to conserve pixel area after rectification. But essentially both should work, its just a question of what the different algorithms are expecting. ScaViSLAM uses a hierarchy of images, so recalculates the camera parameters for each scale, which it then needs the zero disparity matrix. You can do the same calculations for non-zero disparity, but it was easiest to get started using the CV_CALIB_ZERO_DISPARITY flag.

Left and right images with non-zero disparity calibration and the resulting disparity map. More area of the captured images is preserved. The disparity map looks quite difference due to the different Q matrix that will reproject the points

I cam across this blog: http://robotpingpong.blogspot.de/2013/03/small-progress-in-triangulation.html. Here the results for the rectification are much more warped. I guess because his 2 PS3 cameras are not so closely arranged. The raspberry pi camera gives good results here because you can mount then closely (6.5cm apart). His cameras I think were 50cm apart.

Working with raspivid for low latency and other issues with compression

So after I got ScaViSLAM working, I had an issue with getting my raspi stereo camera working at 640x480 as expected (I wanted to use 640x480 because the perf at 1920x1080 was too slow, but the perf would probably get better in a release build). The issue was that I was getting bursts of 17 frames in the stream. Instead of the h264 stream from the pi delivering one frame as often as possible, it was sending 17 frames in one packet. This meant that there was a latency of 33ms*17=500ms. This is not so usable. I eventually analyzed the H264 stream by detecting the NAL markers, which showed that the H264 decoder was decoding 17 frames for a I frame packet (buffer starting with 0x00000125). An I frame is a single frame that is only compressed against itself, there is no dependency to previous frames. A good reference for the H264 basics is
http://keyj.emphy.de/files/projects/SA_paper.pdf. I don't know if another decoder could handle this better or if this is normal, but then the solution was to increase the frequency of the I frames. using "raspivid -g 1" the I frames are then sent every frame. Then the problem is solved because then I receive as new a frame as fast possible and my video queue is always at most 1 deep (unless whatever algorithm that is running on the server is too slow to consume the stream).

I added code in
https://github.com/arcanon/raspbot/blob/master/h264decoder.h
to detect the NAL markers and also check how full the frame queue was.


 if ((data[i] == 0) &&
  (data[i+1] == 0) &&
  (data[i+2] == 0) &&
(data[i+3] == 1))
{
      NALCount++;
      nalFound = true;
....
}
and
if ((m_reader->framesInQueue()- start) > 1)
{
   falseCount+= (m_reader->framesInQueue()- start);
   printf("%s, error frames more %d size %d fa %d, nc %d fc %d\n", this->m_vid_server.c_str(), (m_reader->framesInQueue()- start), prevSize, framesAdded, NALCount, falseCount);    }

Saturday, December 7, 2013

A step towards SLAM - compiling ScaViSLAM for windows

So after much fighting and nail biting I have managed to get ScaViSLAM to compile on windows (why not just use linux? I don't really have a linux system right now and generally have fewer issues with windows). I had to fix a lot of cmake files and figure out how libraries should be compiled with the correct runtime libraries (need to match for all dependencies). I have tested it with the new college data. The main code base is at:
   https://github.com/strasdat/ScaViSLAM
and my fork is at
    https://github.com/arcanon/ScaViSLAM

I have also made a fork of the vision tools at https://github.com/arcanon/VisionTools to update some things to support windows. I guess other packages also need some special changes and those are not all there yet.

Runs at 2-3 fps with a debug build (all dependent libraries as well). Very, very fast :). on the default width = 512 and height = 384.

Proof that ScaViSLAM runs on windows

So the next step is to plug the previous stuff I did with the stereo camera into this system from http://subgroup-ash.blogspot.de/2013/11/stereo-camera-with-block-matching.html. The UI of ScaViSLAM is also really nice, all GL and has perf and nice input fields. So the final system I create would change to this system probably.

To plug in my stereo camera, I have to work out the relation from the stereo matrices calculated in the stereo calibration because ScaViSLAM uses a slightly different start point. I think there the stereo camera is always calibrated and the camera "baseline" is well defined. Should work, but need to figure out the maths that gets the right values at the right places.

In general the next steps could be:
   1.) Compiling a complete ROS (robot operating system). This could be linux or windows, mostly just important to have access to the rich environment that ROS offers. such as the PTAM implementation.
   2.) Think about porting ScaViSLAM to that environment.
   3.) Think about using PTAM (http://wiki.ros.org/ethzasl_ptam/ptam)

Tuesday, November 19, 2013

Stereo camera with block matching

So I managed to start using my stereo camera to seeing some depth and got the first steps working to generating a point cloud of the scene.
Top row: Left and right images (rectified). and disparity image is lower left using the block matching algorithm.
The new camera uses adjustable screws, see image below:
New camera setting with adjustable screws
I used the code/app from http://blog.martinperis.com/2011/01/opencv-stereo-camera-calibration.html to calibrate the cameras and work out there matrices/ rectifying values for each feed. Once the feeds are rectified (another way to describe this would be to normalize the images, so that they can be interpreted correctly) then the block matching algorithm can be used to determine the disparity map. Next would be to calculate the actual distance of each point from the camera and then we can generate a point cloud. The algorithm only runs at about 5 fps right now on the HD frame on my NVS 4200. I checked with NVIDIA nsight profiling and the block matching kernel takes 188ms, which is the reason for the poor perf. See image below

Profile using NVIDIA nsight of stereo block matching
 As always, the code is up at https://github.com/arcanon/raspbot. I know, it won't really compile, which I hope to fix. Next I want to generate the point cloud and then I will make everything easily compile-able. well as easy as you can get, because its using a whole bunch of complicated libraries...