TinyURL widget - shorten your URL's for free!

Enter a long URL to make tiny:

Thursday, November 19, 2015

Ring Buffers in GNU C from Learn C The Hard Way



I took the ring buffers exercise from  http://c.learncodethehardway.org/book/
and I fixed it and expanded it. Not that it wasn't a good starting point but it was a good learning function list I didn't know how ring buffers worked.  I wanted to understand how ring buffers work and they do make sense for a lot of string reads and writes.

I had to fix some faulty logic in the functions, and I  took out the references to the bstring library in favour of GNU C and I put it all into an autotools format like I do.  In this way, one can expand and improve the code for open source applications. Of course then I open source the modified version and put in on GIT.



I had one comment about his code.


Note; one way to fool a compiler is to put function calls into define statements.  Example from his code:

#define RingBuffer_puts(B, D) RingBuffer_write((B), bdata((D)), blength((D)))


Now if this is the only reference to the bstring library, then it might be missed by compilers. GCC choked on this and missed it up until library linking. Otherwise it was a well written library function set.

My ringbuffers expand the original thinking.

 In Zed A. Shaw's original string of chars case the ring buffer is a conceptual ring of memory each one memory space wide.  Increase one memory increases one char.   But in my version there is an analog of a ring buffer of ROWS as the memory element in a ring where the row size and element size are arbitrary and determined by type. This allows one to conduct matrix and vector multiplications to them with pointers aimed at specific rows.


You can find my ringbuffers version here.  GNU Ring Buffers

Autoversion under Autoheader / Autoconf / Automake under git repositories


 https://www.gnu.org/graphics/heckert_gnu.small.png

There is a way to make the library update it's version number automagically is to use the

git describe --tags

function to get the current library version, the GPG signage, and store that value in a temporary file that you shouldn't track inside the repository.


In configure.ac:

AC_INIT([library], m4_esyscmd([build-aux/git-version-gen.sh .version]), [email@shaw.ca])
 Add this line to update the library version from a shell script. There is an unquoted version of m4_esyscmd_s that strips newlines but I found storing straight to file did not add any.

I got this from libqb's git repository but I modified their file to include git describe --tags rather than the original version.


Inside Makefile.am I added .phony targets unlike how libqb did with this:

.version:
    git describe --tags > build-aux/.version
dist-hook:
    git describe --tags  > $(distdir)/.tarball-version

.PHONY:  .version

commit:
    DATE='date'
    git commit -m  " $DATE "
    git push
    
.PHONY: commit

to make a version that then gets read by the autoheader when it sets up configure.

I added an auto commit as well but I may not use that. 

I added another shellscript to the top folder for priming the pump and inserting the first version number into the .version file.

#!/bin/sh
# uprev.sh - shell script to update the latest library tags.

git describe --tags > build-aux/.version

Friday, November 13, 2015

efunda: engineering fundamentals


I found this great website today, and used a couple of their java-script plotters to explain parametric equations. 

efunda stands for engineering fundamentals. 

Here is an excerpt from their website:

eFunda stands for engineering Fundamentals. Its mission is to create an online destination for the engineering community, where working professionals can quickly find concise and reliable information to meet the majority of their daily reference needs.
eFunda is all about the basics, for most part, that means college level material covered in engineering schools. If you practice engineering, more often than not you would find yourself searching for something you knew but could not quite remember. eFunda wants to be your reminder of these formulas. Not only that, eFunda wants to tell you exactly under what conditions those formulas apply, so you don't have to read an entire chapter of the good old textbook.

http://www.efunda.com/about/about.cfm


This is the java applet I was playing with.

Friday, October 16, 2015

Lyx can't find LaTeX classes: delete .lyx in home directory



Lyx can't find LaTeX classes  is the same problem in Ubuntu 15.04

New versions of Lyx don't play well with cached variables from older versions, so delete .lyx in home directory from outside lyx and restart it.

Thursday, October 15, 2015

Network socket configuration Linux in C



I have had the pleasure of configuring:

  • IP sockets for reading and writing
  • multi-cast IP sockets for reading and writing
  • broadcast IP sockets for reading and writing

in the span of about 4 weeks, intermittently.

Here is what I have learned:

There is a lot of confusion based on the server/ client mentality. Clients can read and write; and so can servers so this doesn't always fit.   Because people construct half-assed examples usually using only one type of interface it's not clear from these what the patterns are. For example, one source example uses the loopback address but the coder didn't realize this was a special version of the process and his code spat out errors when moved to a generic setup with 3 addresses.

Instead: think of it in terms of LOCAL address and FOREIGN address rather than client / server model.

You need to setup your local configuration first so you need SOURCE ADDRESS data.  When you go to interact on the internet (READ/ WRITE) you need the DESTINATION ADDRESS data.


Every IP transmission involves a DESTINATION and SOURCE IP address.  The context of which is which depending on if you are reading or writing and if that changes.  So there can be confusion as to which gets configured. And if you want a GROUP multicast you need 3 IP ADDRESSES. The last is the group you are joining.



When making a reading socket or writing socket you must bind it to the LOCAL (what they call the DESTINATION  address from the sending / server side)  IP Address of the machine you are on so the network interface recognizes it.  Of course, what if you have multiple interfaces and you don't know a priori which one to bind to? Simple: bind to IN_ADDRANY as a way to leave that to the network routing rules.  You need to bind or there is no network traffic to your socket as a reader.  You can get away with not binding writers if you decide to change the address on a packet by packet basis. But I would bind for a session in most cases, it also brings up problems with your code in configuration that may stop errors later. As a wiser older Unix programmer, I would rather bind to a session, fork and kill rather than reusing it...

When you begin to read from a specific FOREIGN (what they call the SOURCE address from the sending side  ) IP ADDRESS with a command like recvfrom() your command will block until data is ready. For a DATAGRAM it is mainly all or nothing so it won't return until the data packet is complete.  If you run your program and there is no data reading, it means you are not configured correctly.

When multi-casting, you need to not only bind to the local address, you need to set the group address and the local address in a group request and then JOIN the multicast group.  There is no LEAVE message so your host can quit anytime.

When you are reading broadcast packets, you don't have to set permission to broadcast, just configure, bind, and recvfrom().

When you are writing broadcast packets on Linux, you MUST setsockopt to BROADCAST something like:
      int broadcast = 1;
      z = setsockopt(s, 
                     SOL_SOCKET, 
                     SO_BROADCAST, 
                     &so_broadcast, 
                     sizeof( so_broadcast) );

Otherwise, the packets won't be sent.

In all cases with a modern kernel, I have had no problems with the networking packets arriving timely. It is the configuration of the code that has taken the longest.

Monday, October 12, 2015

DP-46 pan tilt driver library

DP-46 C driver library: If you need a driver to operate the DP-46 pan tilt units, I have separated out the DP-46 library from the libdrdc standard library into a stand alone. It used to be the DirectPerception(DP) module and now it is owned by FLIR.


http://www.flir.com/mcs/view/?id=53707&collectionid=581&col=53710
If you want to use it in RTEMS and or Linux it will configure and make just fine.  It uses the GNU Automake/ Autoconf / Libtool auto-tools.

It is hosted on github:

https://github.com/DaemonDave/dp46

I was authorized by the business development office to release the code into the public domain.

Tuesday, October 6, 2015

A good tutorial on multicast UDP packets

http://www.tenouk.com/Module41c.html

 http://www.tldp.org/HOWTO/Multicast-HOWTO-2.html

For those looking into multicast, the examples at the above sites work.

For those programming network sockets in C on Linux, the loopback device lo on address "127.0.0.1" is a member of the multicast group "224.0.0.1" under IPV4.

Most people write very focussed simple programs to get the point across / key concepts.

In that case, it takes a while to do something serious based on snippets.

Here is a multi-cast multi-socket that reads from one port and writes to another port using 2 threads and  main as the data passer. The only simplification is a simple custom mutex and microsleep for a proper mutex / event system. Code below:

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h> //for threading , link with -lpthread
#include <string.h>

#define PORT1    100000
#define PORT2     100002
#define MAXMSG  256
#define MAX_STRING  256
#define MAX_DATA     256



//
// GLOBAL VARS
//

// state flags for simple finite state machine
// 
// Ready = 1  ready for NEXT ACTION
// !Ready = 0 not ready for NEXT ACTION
//
// Read
// Compute 
// Write
//  
// data is ready for reading and computing,
// start with read in not ready state
static int data_ready =0;

// computations were done and now
// when the data is ready for writing out
static int compute_ready = 0;

// when there is data to go out, write to output
static int write_ready =0;

// the common addresses for this program
// the reading config
static struct in_addr      local_address;
static struct sockaddr_in     group_address;
// the writing config
static struct sockaddr_in writer_address;
static struct in_addr      writer;

// the input data area 
static char data_area[MAX_DATA];
static size_t data_size;

// the output data area
static char output_area[MAX_DATA];
static size_t output_size;

// Sets up Writer socket including
// Sets up group address
// Sets options
// sets local interface for outbound multicast
// then is ready to writeto interface
int set_writer_socket  (int socket, char local[], char group[], int port) 
{
    /* Initialize the group sockaddr structure with a */
    /* group address of "group" and port "port". */
    // this is the sendto data for multicast packets
    memset((char *) &writer_address, 0, sizeof(writer_address));
    writer_address.sin_family = AF_INET;
    writer_address.sin_addr.s_addr = inet_addr(group);
    writer_address.sin_port = htons(port);
    // the ip address specified 
    // set to reuse address so other sockets can join
    // char type won't work - needed unsigned int as valid argument
    // even though Open Group standard calls for a int
    u_int yes = 1;
    if ( setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)) == -1 )
    {
        perror("setsockopt SO_REUSEADDR");
    }   
#ifdef SO_REUSEPORT
    if (setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, (const char*)&yes, sizeof(yes)) < 0) 
        perror("setsockopt(SO_REUSEPORT) failed");
#endif
    /* Set local interface for outbound multicast datagrams. */
    /* The IP address specified must be associated with a local, */
    /* multicast capable interface. */
    writer.s_addr = inet_addr(local);
    if(setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&writer, sizeof(writer)) < 0)
    {
      perror("Setting local interface error");
      exit(1);
    }
    else
      printf("Setting the local interface...%s\n", local);
    
    
}

// sets up the lower socket specifically for reading
// sets address to ANY
// binds to address
// joins multicast group on address
int set_reader_socket (int socket, char local[], char group[], int port) 
{
    struct ip_mreq group_request;
    /* Enable SO_REUSEADDR to allow multiple instances of this */
    /* application to receive copies of the multicast datagrams. */
    int reuse = 1;
    if(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
    {
        perror("Setting SO_REUSEADDR error");
        close(socket);
        exit(1);
    }
    else
    {
        printf("Setting SO_REUSEADDR...OK.\n");
#ifdef SO_REUSEPORT
    if (setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) 
        perror("setsockopt(SO_REUSEPORT) failed");
#endif       
    }
    /* Bind to the proper port number with the IP address */
    /* specified as INADDR_ANY. */
    memset((char *) &group_address, 0, sizeof(group_address));
    group_address.sin_family = AF_INET;
    group_address.sin_port = htons(port);
    group_address.sin_addr.s_addr = INADDR_ANY;
    if(bind(socket, (struct sockaddr*)&group_address, sizeof(group_address)))
    {
        perror("Binding datagram socket error");
        close(socket);
        exit(1);
    }
    else
    printf("Binding datagram socket...OK.\n");
    /* Join the multicast group "group" on the local "local" */
    /* interface. Note that this IP_ADD_MEMBERSHIP option must be */
    /* called for each local interface over which the multicast */
    /* datagrams are to be received. */
    group_request.imr_multiaddr.s_addr = inet_addr(group);
    group_request.imr_interface.s_addr = inet_addr(local);
    if(setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group_request, sizeof(group_request)) < 0)
    {
        perror("Adding multicast group error");
        close(socket);
        exit(1);
    }
    else
    {
        printf("Adding multicast group...OK.\n");   
    }
   
}
   

// Multicast socket setup options
// set sockaddr structure to a group address and port   - sockaddr_in
// set sockopt for loopback packets or not 
// set in_addr for local interface to outbound multicast datagrams 
//  
// make multicast and loopback
/*! \fn set_socket_options
 * 
 * \var socket - a pre-made socket
 * \var local_address - the local host address to bind to 
 * 
 * */
int set_socket_options (int socket, char local_address[], char group_address[], int port, struct sockaddr_in *addr, struct in_addr * local_addr )
{
    struct ip_mreq mreq;        // multicast request
    struct sockaddr_in *local;      // local address
   
    local = addr;
   
    // clear memory
    memset (  (char *) &local, 0,  sizeof( local ) );
    memset (  (char *) &mreq, 0,  sizeof( mreq ) );
   
    // the ip address specified 
    // set to reuse address so other sockets can join
    // char type won't work - needed unsigned int as valid argument
    // even though Open Group standard calls for a int
    u_int yes = 1;
    if ( setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)) == -1 )
    {
        perror("setsockopt SO_REUSEADDR");
    }   
#ifdef SO_REUSEPORT
    if (setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, (const char*)&yes, sizeof(yes)) < 0) 
        perror("setsockopt(SO_REUSEPORT) failed");
#endif
    // assign socket to local address
    local->sin_family = AF_INET;
    local->sin_port = htons (port);
    local->sin_addr.s_addr = inet_addr(local_address);  

    // bind for single comms at the end of options
    if (bind (socket, (struct sockaddr *) local, sizeof (local)) < 0)
    {
        perror ("bind");
        exit (EXIT_FAILURE);
    }
    else
    {
        printf("+Bound to address : %s port : %d \n", local_address, port);
    }
   
    // change to join multicast
    // Initialize group socket address structure 
    // with a group address and port

    /* Join the multicast group "group_address" on the local "local_address" */
    /* interface. Note that this IP_ADD_MEMBERSHIP option must be */
    /* called for each local interface over which the multicast */
    /* datagrams are to be received. */

    // construct a multicast address structure 
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_multiaddr.s_addr =      inet_addr(group_address); 
    mreq.imr_interface.s_addr =     inet_addr(local_address);
    setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

    // change the default Time To Live to 1 packet or more
    u_char ttl = 1;
    setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
   
    // change to loopback packets for local 
    // it must be setup for a packet to be read by other group members
    u_char loop = 1;
    setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
   
    /* Set local interface for outbound multicast datagrams. */
    /* The IP address specified must be associated with a local, */
    /* multicast capable interface. */
    if(setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, local, sizeof(local)) < 0)
    {
      perror("Setting local interface error");
      exit(1);
    }
   
  return 0;
}




int make_socket (uint16_t port)
{
  int sock;

  // Create the socket. 
  // default protocol is defined as 0
  sock = socket (PF_INET, SOCK_DGRAM, 0);
  if (sock < 0)
  {
      perror ("make_socket: socket");
      exit (EXIT_FAILURE);
  }
  return sock;
}


void write_to_server (int filedes )
{
  int nbytes;
  int flags = 0; 
  
  fprintf (stderr, "Layer: writing: \"%s\" in %d bytes\n", output_area, output_size);
  // sendto (int socket, const void *buffer, size_t size, int flags, struct sockaddr *addr, socklen_t length)
  nbytes = sendto (filedes, output_area, output_size, flags, (struct sockaddr*)&writer_address, sizeof(writer_address));
  if (nbytes < 0)
  {
      perror ("write_to_server: sendto ");
      exit (EXIT_FAILURE);
  }
  else
  {
      printf("write_to_server: wrote %d bytes to interface \n", nbytes );
  }
}
// read in data and update where from in later version
// no state logic internal just a function
// reads from bound port
int read_from_client (int filedes )
{
  char buffer[MAXMSG];
  int nbytes;
  size_t sizebuf;
  int flags = 0;

    sizebuf = sizeof(buffer);
    //recvfrom (int socket, void *buffer, size_t size, int flags, struct sockaddr *addr, socklen_t *length-ptr)
    // 
   
    nbytes = recv (filedes, buffer, sizebuf, flags );
    if (nbytes < 0)
    {
        /* Read error. */
        perror ("read");
        exit (EXIT_FAILURE);
    }
    else if (nbytes == 0)
        /* End-of-file. */
        return -1;
    else
    {
        /* Data read. */
        fprintf (stderr, "Layer: got message: \"%s\" in %d bytes\n", buffer, nbytes);
        // copy to global buffer;
        if ( nbytes <= data_size )
        {
            // copy complete message
            strncpy( (char *) data_area, (const char *) buffer,  nbytes);
            return nbytes;
        }
        else
        {
            // copy complete message
            strncpy( (char *) data_area, buffer,  sizebuf);
            return nbytes;       
        }       
        return 0;
    }
    // never gets here...
    // never gets here...
}

//
// READ THREAD 
//
void *ReadThread( void * in)
{
    int sock = *(int*)in;   
    useconds_t usec;
   
    usec = 50; // 50 usec 
    while (1)
    {
        if ( (( data_ready == 0 ) ||  ( write_ready == 1 ) )   )
        {
            read_from_client (sock );
            // enable computation
            data_ready = 1;
           
        }
        usleep( usec );     
    }
}

//
// WRITE THREAD
//
//
void *WriteThread( void * in)
{
    int sock = *(int*)in;
    useconds_t usec;
   
    usec = 50; // 50 usec 
    // delay thread operating
    usleep( usec );
    while (1)
    {
        if ( (  ( write_ready == 1 ) ) && ( compute_ready == 0)    )
        {
            write_to_server (sock);
            // enable reading 
            write_ready = 0;
            // disable computing
            compute_ready = 0;
        }
        usleep( usec );
     
    }
}





// MAIN
int main (int argc, char**argv)
{
  // LOCAL VARS
  int opt = 0;
  int sock;
  int sock2;
  char server_string[MAX_STRING];
  // set to default ports
  int lower_port = 10000;
  int upper_port = 10002;
  struct sockaddr_in servaddr,cliaddr;
  fd_set active_fd_set, read_fd_set;
  int i;
  struct sockaddr_in clientname;
  size_t size;
  char hostname[1024];
  char  arg_mcaddr_str[16] = "127.0.0.1";   // 239.255.0.0 is the local scope      IPv4 local scope
  char  arg_multicast_group[16] = "224.0.0.1";    // this is the default group for loopback service
  // two threads for use
  pthread_t thread_read;
  pthread_t thread_write;   
  // find out the host address
  struct hostent* h;    
  
    // EXTERNS

 //    
 // EXECUTION
 //
 
     
 // init the global data area
 data_size = sizeof(MAX_DATA);
 output_size  = sizeof(MAX_DATA);
  

 //
 // find the localhost name
 // 

    hostname[1023] = '\0';
    gethostname(hostname, 1023);
    printf("Hostname: %s\n", hostname);
    h = gethostbyname(hostname);
    printf("h_name: %s\n", h->h_name);
  

    // read command line options
    while ((opt = getopt(argc, argv, "a:u:l:h")) != -1) 
    {
        switch(opt) 
        {
            // address to connect to
            case 'a':
                /*  address override */
                strcpy(arg_mcaddr_str, optarg);
            break;
            case 'u':
                upper_port = atoi(optarg);

            break;
            case 'l':
                lower_port = atoi(optarg);

            break;           
            // help case
            case 'h':
            ;
            break;

        }
     }// end while

    // report options
    printf("+connect to server =%s \n", arg_mcaddr_str);   
    printf("+lower port=%d \n", lower_port);
    printf("+upper port=%d \n", upper_port);   
    //
    // clear data areas
    //
    data_size = sizeof(data_area);
    bzero( (void*) data_area, data_size );
    output_size = sizeof(output_area);
    bzero( (void *) output_area, output_size );

    // Create the socket and set it up to accept connections. 
    sock = make_socket (lower_port);
    sock2 = make_socket (upper_port);

    // Setup the socket options
    // set the reader bound to port
    set_reader_socket ( sock, arg_mcaddr_str, arg_multicast_group, lower_port);
    set_writer_socket ( sock2, arg_mcaddr_str, arg_multicast_group, upper_port); 

    //
    // starting threads 
    //
 
    // create read thread
    if( pthread_create( &thread_read , NULL ,  ReadThread , (void*) &sock) < 0)
    {
      perror("ReadThread: could not create thread");
      return 1;
    }

    if( pthread_create( &thread_write , NULL ,  WriteThread , (void*) &sock2) < 0)
    {
      perror("WriteThread: could not create thread");
      return 1;
    }

    //
    // MAIN EXECUTION
    //
    useconds_t usec;
    // do computation when ready
    while (1)
    {
        // check for new data, then copy to middle buffer 
        // computation internal
        if ( data_ready == 1 )
        {
            compute_ready = 1;
            data_ready = 0;
            // clear output data
            bzero( (void *) output_area, output_size );           
            // copy data to output if ready for it
            strncpy ( output_area, data_area, output_size);           
        }
        // compute - this time just copy over from data area to output area
        // then trigger write action
        if (compute_ready == 1)
        {
            compute_ready = 0;
            write_ready = 1;
        }
        usec = 50; // 50 usec 
        usleep( usec );   
   
    }
}

Tuesday, September 22, 2015

Online Math Equation Latex Editor: Codecogs

https://www.codecogs.com/latex/eqneditor.php

codecogscom has made an online equation editor for those that want to make math equations using the LaTeX code format using the MathML library.  Output formats include graphics lie GIFs, HTML and URLs to their website.

This is good for sites like stack exhange mathematics.

Wednesday, September 16, 2015

Viewing Wikipedia Math equations on Firefox

Wikipedia has promptly and unceremoniously dropped mathJAX support for wiki pages. For those of us that read math equations this can be a problem. They brought in MathML which is a mark-up version but not everyone can get the images that are sent instead of the equations mathJAX used to convert. If you work somewhere that rejects / filters incoming images for safety reasons then you need a way to convert; just like mathJAX did way back in June 2015.



Scouring the Firefox addons I found a solution.

To see the equations in the new Wikipedia, download this plugin:

Wiki Math Converter

It produced this output

𝐶𝑀𝐴 n + 1 = x n + 1 + n 𝐶𝑀𝐴 n n + 1

Wednesday, September 9, 2015

Technology Readiness Levels -TRL

NASA developed a system / model if you will to describe the readiness of a piece of technology for application.

These technical / application based levels describe how finished a "finished product" is.



http://www.hq.nasa.gov/office/codeq/trl/trlchrt.pdf

When you are trying to decide how ready a piece of technology is for application, you might want to use these levels for yourself. Adopting common conventions like this makes it easier to describe your work in a way others not familiar with can understand by common reference. I am on a big kick for interoperability these days, using common definitions and conventions makes sense in so many ways. Less confusion, more understanding.

Saturday, September 5, 2015

Make Archve From Folder

I wrote a simple - too simple - and poorly written shell script to make an archive file out of a directory containing the name of the computer, the date including hours,  and the project name.

#!/bin/bash
# make archive folder DRE 2015
# Description: makes a named folder and archives it from a GNU Autotools
#specific CVS env vars
#export CVS_RSH=ssh
#export DIR=/usr/local/cvsroot
#export CVSROOT=:pserver:dave@dogma:$DIR

#clean out files if program
make distclean
# remove old copies of archives
rm -fr *.tar.gz

#create dated folder for new mods
storedate=$(date -Ihours)
project=$(printf '%q\n' "${PWD##*/}")
name=$(uname -n)

total="$storedate-$project-$name"
mkdir "$total"
cd "$total"
cp -r  ../* .
rm -fr "$total"
cd ..
tar -czf "$total.tar.gz" "$total"
It isn't the wisest script but people forget the point of unix.  It wasn't to be masters of all aspects of the operating system and tools, but to make the computer do the work so the human didn't have to. I did a quick search for the proper way to copy a folder without itself in the new folder.  I wasn't  happy with what I found so I just used basic commands I knew would work. I don't need to be expert to get it to work. That is a big stumbling block still with Unix. Usability. Knowing the perfect way to do something isn't important as most people assume.

This will waste the computers time executing extra commands.  But so what? It idles 90+% of the time when I am web browsing. It's not in a hurry. I am.

Wednesday, July 15, 2015

Fix your drone; fix landing gear.

Ok so I bought a Horizon hobby Blade 180QX quadcopter and flew it for a few days  before  I smashed it.

The landing gear is too weak for the height this quadcopter falls from.  The major complaint I have is that without cross bracing it buckles if the quadcopter lands sideways and bends in the skids as shown in photo below.

To fix it. You can replace it which is a $5 part.  But then that doesn't change the outcome.
 You want landing gear to absorb the shock but not fold up easily. You will have more down time and less flying with weak original landing gear.

 Notice how bent in the gear are from original to new ones. I went to Michaels here in Chandler Arizona and bought 2 foot 1/8 th inch dowells and some loctite.  I cut two braces and glued them to a new landing gear.  Dry and then install to underchassis.
 Scissors work fine to score and snap dowells and clean the edges.
 Gelled loctite works fast so you can set both and movertical on.
 There the cross braves are added to mske landing transfer more force without snapping the gear.



Monday, May 25, 2015

How to point to a statically compiled variable aiming at dynamic heap object with double indirection and get the value not the address: use a surrogate local variable



typedef struct list_t
{
    //! \var OpenGL-friendly current item count as large integer
    GLuint item_count;   
    //! \var OpenGL-friendly current size_t of total list.
    GLsizei current_max_size;
    //! \var growable - the type of list: growing or not ; in most cases we want growing
    char growable;
    //! \var items  generic variable items without type
    void **items;
    //! \var names variable "items without type"s' names
    char **names;   
}list_t;


void * generic_create_object()
{
    int ** p = malloc (sizeof(int **));
   
    return p;
}
     
Say you statically create a local variable  at compile time

    list_t  test_list;

then make dynamic variables on the heap and aim them into the static structure
 int ** c = generic_create_object();   
        list_add_item(&test_list, c, name);

 How does one get the pointed to value in test_list.items[i]? Not the address?

If you call test_list.items[i] you will get the address.



Simple. make a temp variable in the right syntax and equivalence operation it.

 int ** ptr = test_list.items[i];

printf("list index:%d addr:%u value: %u\n", i, &(test_list.items[i]), **(ptr)  );

Now the pointed to pointed to value will appear properly and the dereference   next to the address will get the right address value.

You can do it by making a temp variable in a local loop like


    int i = 0;
    for (i = 0; i < test_list.item_count;  i ++ )
    {
        int ** ptr = test_list.items[i];
        printf("list index:%d addr:%u value: %u\n", i, &(test_list.items[i]), **(ptr)  );
        if ((i > 5) && (**ptr != 13))
            return -1; // failed to have the right value
    }
and then point at it properly to get the value not the reference. It will be a stack register type cast in the proper variable. It can then be optimized and the compiler will probably remove it.


Complete code:

/***\file test-list.c   *********************************************************
                          
 H.File   $Header:  $
    
 Date Started: -2015

 Date:     $Date:  $
 *
 Author:  $Name:   $

 Purpose:  The list "object" for object-oriented C programming.

 Version:  $Id: doublerow.h,v 1.2 2007/08/24 14:27:29 cvsuser Exp $

 Revision: $Revision:  $
 *
 *
 Log:   $Log:   $
***************************************************************************/

 /*
 * For information on the open science consortium, go to
 *
 * openscienceiscool.blogspot.ca/2014/12/open-science-consortium-beginning.html
 *
 * */

#include "list.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

// This test program is for the underlying list items that are used by the object data ..
// The goal is to understand how this works and then how the second tier functions use it.
/*!
 * typedef struct list_t
{
    //! \var OpenGL-friendly current item count as large integer
    GLuint item_count;   
    //! \var OpenGL-friendly current size_t of total list.
    GLsizei current_max_size;
    //! \var growable - the type of list: growing or not ; in most cases we want growing
    char growable;
    //! \var items  generic variable items without type
    void **items;
    //! \var names variable "items without type"s' names
    char **names;   
}list_t;
 * */

void * generic_create_object()
{
    int ** p = malloc (sizeof(int **));
   
    return p;
}

#define DE
int main (void)
{
    list_t  test_list;
   
    char * name;
   
    int *     pointer;
    int *     pointer2;   
    int      a = 5;
    int     bb = 13;
   
    pointer = &a;
    pointer2 = &bb;   
    name = malloc (sizeof(char)*20);
   
    strcpy( name, "TEST\n");
       
    // makes a list of 10 items  to start that is growable            
    list_make(&test_list, 10, '1');
   
    int ** b = generic_create_object();
    int ** c = generic_create_object();   
    int ** d = generic_create_object();   
   
    *b = pointer;
   
     *c = *d = pointer2;
   
    // add items to the list.
    list_add_item(&test_list, b, name);
    list_add_item(&test_list, b, name);   
    list_add_item(&test_list, b, name);       
    list_add_item(&test_list, c, name);       
    list_add_item(&test_list, c, name);       
    list_add_item(&test_list, d, name);       
    list_add_item(&test_list, d, name);       
    list_add_item(&test_list, d, name);       
    list_add_item(&test_list, d, name);       
    list_add_item(&test_list, d, name);       
    // test the grow function
    list_add_item(&test_list, d, name);           
   
                       
    printf( "number of max items count: %d \n", test_list.current_max_size);   
    // show all the indices
    list_print_indices(&test_list);
    //
    printf( "number of list items count: %d \n", test_list.item_count);
    //    simple print out
    list_print_items(&test_list);
   
    // print out all values
    int i = 0;
    for (i = 0; i < test_list.item_count;  i ++ )
    {
        int ** ptr = test_list.items[i];
        printf("list index:%d addr:%u value: %u\n", i, &(test_list.items[i]), **(ptr)  );
        if ((i > 5) && (**ptr != 13))
            return -1; // failed to have the right value
    }
   
   
    // testing the delete functions
    list_delete_all(&test_list);
    // succeed and return 0 success other wise failed beforehand with a 1
    return 0;
}




Wednesday, April 8, 2015

ERROR: Mimetype entry missing or not the first in archive EPUB


When you extract files from an EPUB, fix them and update the archive, that changes the ordering inside.

When you try to submit a revised EPUB, you will get this error:

ERROR: Mimetype entry missing or not the first in archive

To fix it, extract all content and make a new archive with an UNCOMPRESSED mimetype file FIRST like so in Linux/Unix/Apple:

zip -X0 book.epub mimetype

then remove the mimetype from the contents and add the rest like this:

zip -X8Dur book.epub *

That should refresh the order and compress all the other files.

Monday, April 6, 2015

Blogger can show animated GIF of about 12MB

This Animated GIF is about 12 MB.


Correct Video Sequence Names in OpenShot

I used this post to make this animated GIF:



And this longer one



My previous posts dealt with how to make an animated GIF.

I use openshot to convert a video sequence into a image sequence.

There is a problem with the default naming sequence for openshot.  If you leave it as default it uses the printf number format:

%d

which then puts  __1.png

besides    _100.png

in the sequence.


It is set in the export menu






Instead, increase the number format to have say 4 digits which will be zeros preceding the number like this

%4d

instead of

%d

It is using good ol C printf format so this will make the right sequence like so:








Saturday, April 4, 2015

How to make Animated GIFs Part 2

Ok;


This summarizes the work in past posts, I have made an animated GIF from Call of Duty: Advanced Warfare and I used this post:

http://techdiagnosys.blogspot.ca/2015/04/bash-shell-scripting-read-all-files-in.html


to remove every nth file from a series of PNG files and then I used ImageMagick to convert them into an animated GIF with a reasonable loop speed.

I use this post to convert:

http://techdiagnosys.blogspot.ca/2015/04/how-to-make-animated-gif-using-image.html

I could iterate the .png file reduction by every 3, 4 or 5 files recursively until down to the right size.

I know that blogger can upload files up to 45MB animated GIFs since that's what this one above is.

I hope this helps you and most likely me when I forget how to do this.





BASH Shell scripting: read all files in directory, read files of .suffix, remove the nth file, and output count

BASH is hard for beginners and even pros; unless you were taught all the contexts of data and variables, then it's hard to remember which circumstance makes sense.

The biggest problem with Linux is the powerful tools matched with skimpy documentation.  It resides in the brains of people that forget to pass it on.

Or, perversely, the people that want to hoard the information and make you beg them to reveal it.  I really don't like that attitude from the older SYSV crowd, they seem to be helping by answering questions but it would be better to take the time and write out complete methodical examples. And to explain step by step.

The unix community has a penchant for Munchausen syndrome. It has a need to be asked for help rather than just being helpful.

I would recommend if you are trying to learn to make simple examples with the dangerous parts left out and build up functionality from simple parts.

For these examples, I started making a shell script that could read files, then read certain files, then read and delete certain files.  I made lots of mistakes and the directory was placed in /tmp in case I toasted it.

This script would be good for someone trying to make a GIF from a series of image files. Sometimes people that want to do the functionality but don't want to learn a language this is a good post for that.

I made this really bad animation removing every third file over and over unto a nonsensical mess but it made an interesting GIF.





This shellscript reads all files of any type (not hidden) in the present working directory and echos the file name and increments a counter.

At the end, it outputs the count of files:


#!/bin/bash
x=0
FILES=./*        # BASH resolves variable for you
for  f in $FILES
do
    echo $f        # iterates through and echoes
    ((x++))         # postincrements counter for all type file
done
echo "$x"

You can find this file here

If you want read and count files of a certain .suffix then  send it as a command line argument in this case $1 first arg. NOTE: don't include the '.' !

#!/bin/bash
# this version reads files of a certain .suffix passed as arg $1
x=0
FILES=./*.$1        # BASH resolves variable for you
for  f in $FILES
do
    echo $f        # iterates through and echoes
    ((x++))         # postincrements counter for all type file
done
echo "$x"
You can find this file here

For a good tutorial of if then constructs go here:

If you want to iterate through and remove every nth file in that file list with the suffix, then execute this shellscript:

#!/bin/bash
# DRE 2015 - Thinking, Realizing, Reacting
# this version reads files of a certain .suffix passed as arg $1
x=0
y=0
n=$2
echo $n
FILES=./*.$1        # BASH resolves variable for you
for  f in $FILES
do
    echo $f        # iterates through and echoes
    ((x++))         # postincrements counter for all type file
    if ((x%n == 0))  ;  then  # test for file increment and execute
        rm -f "$f"   # you need to quote filename variable to get name in proper format to rm command
        ((y++))    
    else
        :   # this is the BASH no operation (NOOP) command
    fi
done
echo files count: "$x"
echo files removed : "$y"
You can find this file here


TO RUN THIS COMMAND, Open a SHELL and type:

sh ~/shellscripts/remove_n_file.sh png 3

( I put all my shell scripts into a common folder shellscripts. Your placement will vary command. )

inside the directory with a series of PNG image sequence files.  This will look for all .png files and removes every 3rd one. mod 3 for this example.

So if you have 2100 files, the first run through of the script would remove about 700 files.  Note that the contents would still be in sequence.  Then, if you wanted to reduce them further just do this again and again and it will remove from the same sequence over and over. You can compensate for varying the loop delay time with Image Magick. Of course if you thin out too many images the sequence will go really fast and may not work right.  This is the art of the process. 

To make a good motion animated GIF you should be looking at a sequence that's a few second in video length.

This shell script takes 2 variables, the suffix first and then the number file to delete.

Note for sake of simplicity there is no error checking here so you can destroy the contents you are working on. Use against a copy folder!  This is a tutorial not a production shell script.

The tricks are you use the (()) C argument mode to evaluate math properly, so you don't need $variable names but the actual variables.

You need to quote the $f filename to send it resolved to remove command.


NOTE:

This shellscript DOES work on ubuntu 14.10 with:

 bash --version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

If you change the downloaded file permission to executable

chmod u+x remove_n_file.sh

to allow it to run and run without sh

~/shellscripts/remove_n_file.sh png 3

and it does work on Fedora 20 with:

 bash --version
GNU bash, version 4.2.53(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


How to make an animated GIF using Image Magick

HOW TO MAKE SIMPLE  ANIMATED GIF USING CONVERT - IMAGE MAGICK




This is for someone using ImageMagick on linux, sorry Windows people I don't know if this works on Windows.

1. Create a sequence of same size jpg files.



2. Move all files into one directory

3. Rename then to a sequence name%d.jpg file list


4. Then run from command line:

convert -delay 100 -loop 0 image*.jpg animation.gif

http://books.google.ca/books/about?id=_CbHBwAAQBAJ&redir_esc=y
Animation.gif with .png files

Thursday, March 26, 2015

Gnu Make real-time variable static library construction and testing

You can do a lot without automake, but I don't recommend it. In some cases it makes sense if you can isolate your work in development to simple lightweight fast code that doesn't have a lot of dependencies.

Also, GNU make is hard to learn without real examples.  The GNU suite of tools, GCC included is very steep learning for a beginner.  A lot of time is spent on head smashing against grammar errors.  Gnu tools are very unforgiving. I have learned by error and pattern more times than not.

So when I do things that are complicated, and use the special rules, I will try to pass on what I know by writing them down.

This code will appear in marrspace in the future.   I have off-lined this development in order to make sure this piece works first.  Once that's accomplished I will install the working code inside marrspace.

# Makefile for the messages that come out of the visual information message packages

# DRE 2015
# This library makes a C protocol buffers implementation for other code elsewhere.
# This nasty little subdir should be kept isolated as it deals with agnostic elements of a message protocol.
# that does not need localization because it is stripped away from operating system issues.
# It is a self contained message generation folder.
# that can make a library of messages if needed that can be referenced elsewhere.
# The makefile need only append more message types into MESSAGES and
# that will make a longer list of message elements compiled together.
# I made a test application for atof when it didn't convert large double values correctly, to check if you need strtod
#
# Self-contained testing can happen between msg1 that pipes to msg2 a generic message
# If the output looks like the input, the messages are working

MESSAGES = VMessage  FMessage  GMessage  LMessage  PMessage  MMessage WMessage

# redefine all message types as the final protocol-buffer autogenerated pb-c.c files that might not be present when the folder is spawned

MESSAGES_C = $(MESSAGES:=.pb-c.c)

# redefine all message types as protocol .proto files from the same root. This will tell you if the message is missing as well.
MESSAGES_P = $(MESSAGES:=.proto)
# redefine messages as object files.

MESSAGES_O = $(MESSAGES:=.pb-c.o)


CC = gcc
AR = ar
CFLAGS     = -I./
OBJ1     = msg1.o
OBJ2     = msg2.o

LIBS = -lprotobuf-c -lm libmessage.a

msg1: $(OBJ1)
    $(CC) -o $@ $^ $(MESSAGES_C) $(CFLAGS)  $(LIBS)

msg2: $(OBJ2)
    $(CC) -o $@ $^ $(MESSAGES_C) $(CFLAGS) $(LIBS)

libmessage:  VisObject.o $(MESSAGES_O)
    $(AR) ru  libmessage.a  VisObject.o $(MESSAGES_O)


test-atof: test-atof.o
    $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

.PHONY:    clean check


list : $(MESSAGES_P)
    echo $(MESSAGES_C)
    echo $(MESSAGES_P)

messages :    $(MESSAGES_P)
    protoc-c --c_out=./ $(MESSAGES_P)

all :  messages  libmessage msg1 msg2

# tester recipe that creates the test case and sends data through and outputs to file test.out so you can review what was sent.
# we use the special case $(shell command) to send BASH a command line to execute.
check:  msg1 msg2
    $(shell ./msg1 1  23.44  34.55666 56566 43433.3 4545454.4 444444.5 9999999999999999999993.0  45   -7.777777777777777e1 | ./msg2 2> test.out  )




clean    :   
    rm -f *.o
    rm -f *.pb-c.c
    rm -f *.pb-c.h
    rm -fr *~
    rm -f msg1 msg2 test-atof
    rm -f test.out
    rm -f libmessage.a


    This is a modified version of yesterday's work.  Instead of making two programs with all the C object files, I use the AR archive program to make a static library that is linked to the programs.  Why static? Because it will be small and you want it to run fast.  Why make only? Because this code is agnostic because it references library dependencies that aren't unique apart from protocol buffers that are made generic.  Why C? Because the production system needs to run extremely fast. The marrspace library adds vertexes and lines to a rolling real time model of the visual scene.  I detest bloated C++ code so I am taking more time to make functions that will execute minimally.



To test the output of the piped message from msg1 to msg2 by using a phony recipe that runs the two programs with a canned set of data. 


I use the special $(shell) command to run in the shell's environment.  To call it phony means that make won't look for output files from the shell execution.  There is a test output .out made so one can review the messages passed and converted.  That's all you need for a production library.  GNU make is more powerful than people imagine, if only people have the time to read all the documentation.


   
   

Tuesday, March 24, 2015

Making auto substitutions in GNU Makefiles

One of the skills I stumbled with being a self-taught Unix programmer, was to uncover all the tricks that Unix style development has. 

If you make a makefile, and want to autocreate variables from a pattern, then you can use make's variable substitution to keep a growing list without needing automake. 

There are of course many places where the need for many libraries and so on won't make that a portable thing, you will have too many dependencies that creep in and spoil the simplicity. 

I was hit with the need to make a simple self-contained message protocol to pass data, and I started with Google's protocol buffers in C. 

Since those are simple self-contained protocols that pass data and depend on one library, I thought I could just get away with a Makefile for this subdir:

Here is the reference for substitution:

https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html#Substitution-Ref

here is the simple Makefile


MESSAGES = VMessage  FMessage  GMessage  LMessage  PMessage  MMessage WMessage
# redefine all message types as the final protocol-buffer autogenerated pb-c.c files that might not be present when the folder is spawned
MESSAGES_C = $(MESSAGES:=.pb-c.c)
# redefine all message types as protocol .proto files from the same root. This will tell you if the message is missing as well.
MESSAGES_P = $(MESSAGES:=.proto)

CC = gcc

CFLAGS     = -I./
OBJ1     = msg1.o
OBJ2     = msg2.o

LIBS = -lprotobuf-c

msg1: $(OBJ1)
    $(CC) -o $@ $^ $(MESSAGES_C) $(CFLAGS)  $(LIBS)

msg2: $(OBJ2)
    $(CC) -o $@ $^ $(MESSAGES_C) $(CFLAGS) $(LIBS)


list : $(MESSAGES_P)
    echo $(MESSAGES_C);
    echo $(MESSAGES_P);

messages :    $(MESSAGES)
    protoc-c --c_out=./ *.proto

all : messages msg1 msg2

.PHONY:    clean

clean    :  
    rm -f *.o
    rm -f *.pb-c.c
    rm -f *.pb-c.h
    rm -fr *~



To explain,   I define a message xMessage, and then the variable is subtstituted into $MESSAGES_C which places a .pb-c.c suffix onto the name of every message. 

The command

MESSAGES_C = $(MESSAGES:=.pb-c.c)

 defines a variable that takes every MESSAGES and adds a .pb-c.c variant that will be the autogenerated .c file from  'make messages'. These are generated from the protoc-c command.

The command

MESSAGES_P = $(MESSAGES:=.proto)
makes a similar .proto file variable if the prototype does not exist.  Why is this useful? Because what if you hadn't made all the messages first, or they were improperly defined, then your message library would be incomplete and might compile and execute leading to undefined behaviour.

One could change it to the other way around but what if I wanted a "ERROR" message as well, they don't have to follow a pattern.  The names don't have to be the same to work.  The library doesn't care but a human reader might.

Then it defines a dependency xMessage.proto which is the message prototype under the protocol buffers system to package a message.

Those are used as dependencies to make a simple message testing program set of msg1 and msg2.


 

Tuesday, March 10, 2015

How to import VCF cards into Mozilla Thunderbird

It seems odd that a major email application can't import the standard contact information file (.vcf) natively.  It's sad but true.

According to this support reply:

https://support.mozilla.org/en-US/questions/994358


vCard or .vcf
Some address book programs use a vCard format, which cannot be imported directly by Thunderbird. You can import both vCard and .vcf using the third-party Thunderbird extension 'MoreFunctionsForAddressBook'.
https://nic-nac-project.org/~kaosmos/morecols-en.html
  • download to desktop or 'downloads' folder on computer.
  • In Thunderbird. 'Tools' > 'Add-ons' to open 'Add-ons manager' in a new tab
  • click on the daisy wheel icon and select 'Install Addon from file
  • locate the .xpi file you downloaded
  • click on Open to install in Thunderbird
Save the vcard / .vcf attachment to eg: desktop
In Thunderbird:
  • open 'Address Book'
  • select the address book you want it to go to
  • 'Tools' > 'morefunctionforAddressBook' > 'Action for contacts' > 'Import vCard/vcf'
  • locate the saved vcard/.vcf file and click on 'Open'

Download the .xpi file form that website address and follow the installation instructions, then you need to restart Thunderbird.

NOTE: You need to save VCF cards to file and import them, this won't work natively from the email itself!!!

 

Thursday, March 5, 2015

How to clean inkscape SVG files

Inkscape sometimes makes SVG files with forbidden elements in them that epub readers might reject, Google epubchecker does not like inkscape:connector commands and fails with:


Error: Unable to sanitize epub

If you run it locally, it will fail with this error:


ERROR: *.svg(147,2722): attribute "inkscape:connector-curvature" not allowed here; expected attribute "class", "clip-path", "clip-rule", "color", "color-interpolation", "color-rendering", "cursor", "display", "externalResourcesRequired", "fill", "fill-opacity", "fill-rule", "filter", "image-rendering", "marker-end", "marker-mid", "marker-start", "mask", "onactivate", "onclick", "onfocusin", "onfocusout", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "opacity", "pathLength", "pointer-events", "requiredExtensions", "requiredFeatures", "shape-rendering", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "text-rendering", "transform", "visibility", "xml:base", "xml:lang" or "xml:space"



To get around this, download and compile SVG Cleaner.


https://github.com/RazrFalcon/SVGCleaner

https://launchpad.net/~svg-cleaner-team/+archive/ubuntu/svgcleaner

You will also need z7a   (7za-full on Ubuntu)

https://www.ibm.com/developerworks/community/blogs/6e6f6d1b-95c3-46df-8a26-b7efd8ee4b57/entry/how_to_use_7zip_on_linux_command_line144?lang=en
 

 

Friday, February 27, 2015

How to make a floating camera in OpenGL using C functions & GL_MODELVIEW matrix.

Here is a program you can compile to teach you how to make a camera float in an OpenGL scene. I won't claim I'm an expert in all things OpenGL, my first attempt didn't work so well.  But I stumbled on some code bits and cobbled them together.

If you want to use projective perspective and GL_MODELVIEW you can simply transform the camera.

Because the model view matrix has x and y along the 2D screen and negative z out from the viewpoint, two rotations have to be backward and all z-values must be negative and then it works as expected. The files have been changed to reflect this.

The GL_MODELVIEW matrix is already in the fixed coordinate frame of the camera. Simple apply a roll pitch and yaw.  I added xyz translations as well.

Oh, and I increase the Field of View (FOV) using Page UP Page DOWN to zoom in and zoom out.

Key Legend

 * Pitch - tilts camera up/down along current viewing port
 * Roll - rolls side to side like you were in a fighter jet
 * Yaw - turns "left" and "right" although technically it's rotation about an axis not turning
 
 * lz - into and out of the screen
 * lx - slide left and right frictionlessly
 * ly - hover or descend without gravity



Yaw   = Left/ Right Arrow
Pitch = Up / Down Arrow
Roll  =  Home / End Key

Z motion =  7 / 1 keys
Y motion = 8 / 2 keys
X motion = 4 / 6 keys

This is Lighthouse3D's original snow man example modified to use free camera rotations. Except his example forces the camera to the same plane.  You can find it here:

http://www.lighthouse3d.com/tutorials/glut-tutorial/keyboard-example-moving-around-the-world/



Here is a link to my C file:  https://www.dropbox.com/s/k7itn9qc45qvkez/opengl-floatingcamera.c?dl=0


/***\file opengl-floatingcamera.c   *********************************************************
                          
 H.File   $Header:  $
    
 Date Started: -2015

 Date:     $Date:  $
 *
 Author:  $Name:   $

 Purpose: Example OpenGL program showing how floating camera in MODELVIEW mode works.
 * It makes a floating camera using the C library and uses keys to change the relative
 * local frame motion of the camera.

 Version:  $Id: opengl-floatingcamera.c,v 1.2 2007/08/24 14:27:29 cvsuser Exp $

 Revision: $Revision:  $
 *
 *
 Log:   $Log:   $
 *
 * \brief
 * This file modifies the original file to demonstrate how to make a floating camera.
***************************************************************************/
// global library includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/freeglut.h>
#include <GL/freeglut_ext.h>
#include <GL/freeglut_std.h>



// angle of rotation for the camera direction
float angle=0.0;
// actual vector representing the camera's direction
float lx = 0.0;
float ly = 0.0;
float lz = 10.0f;

// camera angles
float yaw = 0.0f;
float pitch = 0.0f;
float roll = 0.0f;


// Field of View
float FOV = 45.0;

// window dimensions
float w_height;
float w_width;


// new camera rotation system
void NewCameraTransformation( void );


/*
   Deal with plain key strokes
*/
void processNormalKeys(unsigned char key, int x, int y)
{
   switch (key)
   {
    case 27: /* ESC */
    case 'Q':
    case 'q': exit(0); break;
    case 's':
    case 'S': ; break;
    case 'b':
    case 'B': ; break;
    // translation keys  - pure location displacements
    case '8': ly -= 1; break;
    case '2': ly += 1; break;  
    case '4': lx += 1; break;   
    case '6': lx -= 1; break;
    case '7': lz += 1; break;   
    case '1': lz -= 1; break;   
   
   }
}


/*!  \fn ChangeWindowSize
 *
 * Updated Change size that takes into account modified window size
 *
 * */
void ChangeWindowSize(int w, int h)
{
    float ratio; // aspect ratio

    // Prevent a divide by zero, when window is too short
    // (you cant make a window of zero width)
    if(h == 0)
        h = 1;
    // get current window sizes    - not sent values
    w_width = glutGet(GLUT_WINDOW_WIDTH);
    w_height = glutGet(GLUT_WINDOW_HEIGHT);
    // compute aspect ration at scale 1.0 * width / height
    ratio = 1.0* w_width / w_height;
    // Use the Projection Matrix
    glMatrixMode(GL_PROJECTION);
    // Reset Matrix
    glLoadIdentity();
    // Set the viewport to be the entire window
    glViewport(0, 0, w_width, w_height);
    // Set the correct perspective.
    gluPerspective(FOV,ratio,1,1000);
    // Get Back to the Modelview
    glMatrixMode(GL_MODELVIEW);
}


void drawSnowMan()
{

    glColor3f(1.0f, 1.0f, 1.0f);

// Draw Body
    glTranslatef(0.0f ,0.75f, 0.0f);
    glutSolidSphere(0.75f,20,20);

// Draw Head
    glTranslatef(0.0f, 1.0f, 0.0f);
    glutSolidSphere(0.25f,20,20);

// Draw Eyes
    glPushMatrix();
    glColor3f(0.0f,0.0f,0.0f);
    glTranslatef(0.05f, 0.10f, 0.18f);
    glutSolidSphere(0.05f,10,10);
    glTranslatef(-0.1f, 0.0f, 0.0f);
    glutSolidSphere(0.05f,10,10);
    glPopMatrix();

// Draw Nose
    glColor3f(1.0f, 0.5f , 0.5f);
    glutSolidCone(0.08f,0.5f,10,2);
}

/*! \fn DrawAxes
 * \brief this function draws three multi-coloured lines on the coord origin
 * to mark the location under transformation
 *
 * */
void DrawAxes(void)
{
    // draw white for X
    glColor3f(0.99f, 0.99f, 0.99f);
    glBegin(GL_LINES);
    glVertex3f(-0.0f, 0.0f, 00.0f);
    glVertex3f(1.0f, 0.0f, 00.0f);   
    glEnd();
    // draw red for Y
    glColor3f(0.99f, 0.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex3f(-0.0f, 0.0f, 00.0f);
    glVertex3f( 0.0f, 1.0f, 00.0f);   
    glEnd();
    // draw Blue for Z       
    glColor3f(0.0f, 0.0f, 1.0f);
    glBegin(GL_LINES);
    glVertex3f(-0.0f, 0.0f, 00.0f);
    glVertex3f( 0.0f, 0.0f, 1.0f);   
    glEnd();       
   
}


// new camera rotation system
/*!
 * Camera Rotation based on +- xyz translation and roll pitch yaw rotation
 *
 * The GL_MODELVIEW matrix is already set up to do the transformations for local - camera
 * transformations.  One only needs to load a new identity matrix, conduct the
 * roll pitch and yaw rotations and then the translation.
 *
 * All variables are fixed coordinates relative to the current pose.
 *
 * Pitch - tilts camera up/down along current viewing port
 * Roll - rolls side to side like you were in a fighter jet
 * Yaw - turns "left" and "right" although technically it's rotation about an axis not turning
 *
 * lz - into and out of the screen
 * lx - slide left and right frictionlessly
 * ly - hover or descend without gravity
 *
 * */
// new camera rotation system
/*!
 * Camera Rotation based on +- xyz translation and roll pitch yaw rotation
 *
 * The GL_MODELVIEW matrix is already set up to do the transformations for local - camera
 * transformations.  One only needs to load a new identity matrix, conduct the
 * roll pitch and yaw rotations and then the translation.
 *
 * Details from http://www.songho.ca/opengl/gl_transform.html#example2
 *
 * Note that there is no separate camera (view) matrix in OpenGL.
 * Therefore, in order to simulate transforming the camera or view, the scene
 * (3D objects and lights) must be transformed with the inverse of the view transformation.
 * In other words, OpenGL defines that the camera is always located at (0, 0, 0) and facing to -Z axis
 * in the eye space coordinates, and cannot be transformed.
 *
 * All variables are fixed coordinates relative to the current pose.
 *
 * Pitch - tilts camera up/down along current viewing port
 * Roll - rolls side to side like you were in a fighter jet
 * Yaw - turns "left" and "right" although technically it's rotation about an axis not turning
 *
 * lz - into and out of the screen
 * lx - slide left and right frictionlessly
 * ly - hover or descend without gravity
 *
 * */
void NewCameraTransformation( float  pitch, float  roll, float  yaw,  float  lx, float  ly, float  lz )
{
       
   
    // Position lights, and camera location
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();   
    /*
     * void glRotatef(GLfloat  angle,  GLfloat  x,  GLfloat  y,  GLfloat  z);
     * Parameters angle
     *                  Specifies the angle of rotation, in degrees.
                x, y, z
                    Specify the x, y, and z coordinates of a vector, respectively.
        
               
     *
     * */
    // Euler XYZ RPY notation
    // roll about z
    glRotatef((GLfloat)((roll)), 0.0f, 0.0f, 1.0f);
    // pitch up down around new x
    glRotatef((GLfloat)((-1*pitch)), 1.0f, 0.0f, 0.0f);
    // rotate around new y
    glRotatef((GLfloat)((-1*yaw)), 0.0f, 1.0f, 0.0f);
    // translate
    glTranslatef((GLfloat)(lx), (GLfloat)(ly),(GLfloat)((lz)));
   
}

// Optional draw ground
void DrawGround()
{
    glColor3f(0.9f, 0.9f, 0.9f);
    glBegin(GL_QUADS);
        glVertex3f(-100.0f, 0.0f, -100.0f);
        glVertex3f(-100.0f, 0.0f,  100.0f);
        glVertex3f( 100.0f, 0.0f,  100.0f);
        glVertex3f( 100.0f, 0.0f, -100.0f);
    glEnd();
}
// Optional Draw Snow Men
void DrawSnowMen()
{
    int i;
    int j;
    //Draw 36 SnowMen
    for( i = -3; i < 3; i++)
        for( j=-3; j < 3; j++)
        {
            glPushMatrix();
            glTranslatef(i*10.0,0,j * 10.0);
            drawSnowMan();
            glPopMatrix();
        }   
}

void renderScene(void)
{
    float camera_viewx;
    float camera_viewy;   
    float camera_viewz;
   
    float camera_view_normalx;
    float camera_view_normaly;
    float camera_view_normalz;
   
    // Clear Color and Depth Buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
    // new free floating camera
    NewCameraTransformation( pitch, roll, yaw, lx, ly, lz    )    ;

    // Draw ground
    //DrawGround();   
    // Draw Snow Men
    DrawSnowMen();
    // draw axes example
    DrawAxes();
   
   
    // final step
    glutSwapBuffers();
}


// modified to control angles as well as translation
void processSpecialKeys(int key, int xx, int yy)
{

    float fraction = 0.1f;

    switch (key)
    {
        /*  Control Camera Roll */
        case GLUT_KEY_HOME :
            roll += 0.1;
            break;
        case GLUT_KEY_END :
            roll -= 0.1;
            break;           
        /*  Control Camera Yaw */
        case GLUT_KEY_LEFT :
            yaw += 0.1;
            break;
        case GLUT_KEY_RIGHT :
            yaw -= 0.1;
            break;
        /* Control camera pitch  */
        case GLUT_KEY_UP :
            pitch -= 0.1;
            break;
        case GLUT_KEY_DOWN :
            pitch += 0.1;
            break;
        /* change the field of view on next resize  */
        case  GLUT_KEY_PAGE_UP:
            FOV +=5.0;
            // hard limit to 90 degrees
            if (FOV >91.0) FOV=90.0;
            ChangeWindowSize(320,320);
            break;
        /* change the field of view on next resize  */           
        case  GLUT_KEY_PAGE_DOWN:
            FOV -=5.0;
            // hard limit to 2 degree
            if (FOV < 4.0) FOV = 2.0;
            ChangeWindowSize(320,320);           
            break;                   
       
    }
}


int main(int argc, char **argv)
{

    // init GLUT and create window

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(320,320);
    glutCreateWindow("Lighthouse3D - GLUT Tutorial");

    // register callbacks
    glutDisplayFunc(renderScene);
    glutReshapeFunc(ChangeWindowSize);
    glutIdleFunc(renderScene);
    glutKeyboardFunc(processNormalKeys);
    glutSpecialFunc(processSpecialKeys);

    // OpenGL init
    glEnable(GL_DEPTH_TEST);

    // enter GLUT event processing cycle
    glutMainLoop();

    return 1;
}