MediaWiki API result

This is the HTML representation of the JSON format. HTML is good for debugging, but is unsuitable for application use.

Specify the format parameter to change the output format. To see the non-HTML representation of the JSON format, set format=json.

See the complete documentation, or the API help for more information.

{
    "batchcomplete": "",
    "continue": {
        "gapcontinue": "Reed_Switch",
        "continue": "gapcontinue||"
    },
    "warnings": {
        "main": {
            "*": "Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes."
        },
        "revisions": {
            "*": "Because \"rvslots\" was not specified, a legacy format has been used for the output. This format is deprecated, and in the future the new format will always be used."
        }
    },
    "query": {
        "pages": {
            "3262": {
                "pageid": 3262,
                "ns": 0,
                "title": "Reading RFID tags",
                "revisions": [
                    {
                        "contentformat": "text/x-wiki",
                        "contentmodel": "wikitext",
                        "*": "== Overview ==\n\n[[Image:rfid_pic.jpg|right|thumb|400px|Image from [https://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/txtSearch/rfid/List/1/ProductID/441/Default.aspx?SortField=ProductName%2cProductName Parallax.]]]\n\nRadio Frequency Identification (RFID) is a generic term used to describe technology that uses radio waves to identify objects via transponder tags.  The tags emit a radio frequency that is a unique serial code which is its identification code.  This is read by an RFID reader.  Active RFID transponders contain their own battery supply which allows a constant signal, but they \nare bulky and expensive, therefore they are usually restricted to larger objects such as crates for shipping supplies.  With passive RFID transponders, the RFID reader emits a radio frequency which causes a coiled antenna in the RFID transponder tag to \ngenerate a magnetic field which powers the RFID transponder.  This generated power allows the RFID transponder tag to emit its \nspecific radio frequency to the reader which it uses to identify the tag.  The passive tags are cheaper then active tags, and require no power unlike the active tags.\n\n\nThe mechatronics lab uses the Parallax RFID Card Reader, Serial (#28140) and kit [https://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/txtSearch/rfid/List/1/ProductID/441/Default.aspx?SortField=ProductName%2cProductName].  The reader reads passive RFID transponder tags up to 4 inches away from the reader.  The RFID communicates using rs232 and transmits a 12-byte ASCII string.  The first byte (start byte) is always $0A and the last byte (stop byte) is always $0D.  These bytes are represented as characters not integers.  The 10 middle digits are the 10 unique id digits that are unique to every RFID transponder.  It outputs at a baud rate of 2400.\n\nNote: The PIC waits for an RF signal and will not proceed until a tag is placed within reading range.\nFor a more extensive summary on how RFID in general works, see the [http://electronics.howstuffworks.com/rfid.htm How Stuff Works page on RFID tags.]\n<br>\n<br>\n<br>\n\n== Circuit ==\n\n[[Image:rfid_diag.jpg|right]]\n[[Image:rfid_cirt.jpg|right|thumb|400px|]]\n\nIt is very simple to connect the RFID reader to the PIC.  All you need to do is plug the RFID reader into your breadboard and then connect the inputs to the RFID reader to the PIC.  In our example we powered our RFID reader off the PIC, so we connected the VCC to the +5 on the PIC and the GND to the ground on the PIC.  The RFID reader needs to be powered by a 5V source.  Its specified limits on input voltage are 4.5V to 5.5V.  The RFID reader is enabled when the /ENABLE pin is set to ground, and not enabled when it is set to 5V.  A red light turns on when it is enabled, and a green light turns on when it is not enabled.  When the RFID read is idle, it uses 10mA of current, while when it is active, it uses 100mA of current.\n\nWe used the [[PIC_RS232|TTL-232R cord]] to output the RFID tag from the PIC to the PC as a proof of concept for our setup.  For more details on how the [[PIC_RS232|TTL-232R cord]] works, see the [[PIC_RS232|TTL-232R page]].\n\n*As a way to check the RFID reader, it can be directly connected to the [[PIC_RS232|TTL-232R cord]] and read to the PC.  Similar to above, the yellow wire of the [[PIC_RS232|TTL-232R cord]] connects to the SOUT of the RFID reader and the black wire goes to the common ground.  \n\n\n\n\n\n\nA summary of the connections:\n\n RFID SOUT to PIC RC7\n RFID VCC to PIC +5 \n RFID GND to PIC GND\n RFID /ENAMBLE to PIC RC7\n PIC RC6 to TTL-232R Yellow Wire (note: this is output signal to view on PC)\n PIC GND to TTL-232R Black Wire (note: this is connecting the grounds together)\n\n\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n<br>\n\n== Code ==\nThe following code reads in the RFID tag from the RFID Card Reader and then writes it out to a ps232 connection.  In our tests we used a TTL-232R with a baud of 2400.  This allows you to view the RFID unique ID on the computer.  This code is a great way to check that your PIC is reading the RFID tag correctly, since it outputs the tag's unique ID to the computer.  This issue with the code is that you have to click reset in between uses, and cannot run though a while loop.\n\n /*\n    RFID_Input.c James Cooper and Chris Ryan 2009-2-04\n    Read in from RFID Card on pin RC7\n    Write out RFID unique number on pin RC6\n */\n #include <18f4520.h>\n #fuses HS,NOLVP,NOWDT,NOPROTECT        \n #use delay(clock=40000000)         // 40 MHz crystal on PCB\n #use rs232(baud=2400, UART1)      // hardware UART; uses  RC6/TX and RC7/RX\n \n int16 i;\n char value[12];//12 values of tag ID\n \n void main() {\n      output_low(PIN_D7); //Set enable to low\n \n      for (i=0; i<12; i++) { //read in the 12 values\n            value[i]=getc();\n      }\n      printf(\"rfid# %c %c %c %c %c %c %c %c %c %c \\r\\n\", value[1] value[2] value[3] value[4] value[5] value[6] value[7] value[8] value[9] value[10]); //display 10 unique id digits\n }\n\nIn order to see how to read the printf output, please see the [[PIC_RS232|TTL-232R page]].  Be sure to use a baud rate of 2400 and not the baud rate used the example on that page.\n\nNote: The values read in are characters, not integers, so be sure to take that into account in the coding.  Also, the 10 unique id digits are value[1] through value[10].  Value[0] is the beginning character that always stays the same, and value[11] is the end character that always stays the same.\n\n== Additional Code ==\n\nThe following code is an example of the RFID value running repetitively, lighting a specific LED (D3, D4, D5, D6).\n\n /*\n    RFID_Input.c James Cooper and Chris Ryan 2009-2-11\n    Read in from RFID Card on pin RC7\n    Prints out lights to pins D3, D4, D5, and D6 depending on which RFID tag is near\n */\n #include <18f4520.h>\n #fuses HS,NOLVP,NOWDT,NOPROTECT        \n #use delay(clock=40000000)         // 40 MHz crystal on PCB\n #use rs232(baud=2400, UART1)      // hardware UART; uses  RC6/TX and RC7/RX\n \n int16 i;\n char value[12];//12 values of tag ID\n \n void main() {\n      output_low(PIN_D7); //Set enable to low\n \n    while (true){\n      for (i=0; i<12; i++) { //read in the 12 values\n               value[i]=getc();\n      }\n      \n      //light pin for if value matches RFID value for tag\n      output_low(PIN_D6);\n      output_low(PIN_D5);\n      output_low(PIN_D4);\n      output_low(PIN_D3);\n      if(value[1]=='3' && value[2]=='6' && value[3]=='0' && value[4]=='0' && value[5]=='6' && value[6]=='2' && value[7]=='1' && value[8]=='0' && value[9]=='6' && value[10]=='9'){\n      output_high(PIN_D6);\n      }if(value[1]=='0' && value[2]=='4' && value[3]=='1' && value[4]=='5' && value[5]=='D' && value[6]=='9' && value[7]=='0' && value[8]=='4' && value[9]=='A' && value[10]=='0'){\n      output_high(PIN_D5);\n      }if(value[1]=='0' && value[2]=='4' && value[3]=='1' && value[4]=='6' && value[5]=='2' && value[6]=='C' && value[7]=='A' && value[8]=='1' && value[9]=='1' && value[10]=='2'){\n      output_high(PIN_D4);\n      }if(value[1]=='1' && value[2]=='7' && value[3]=='0' && value[4]=='0' && value[5]=='7' && value[6]=='D' && value[7]=='4' && value[8]=='0' && value[9]=='C' && value[10]=='5'){\n      output_high(PIN_D3);\n      }\n    }\n }\n\n== Further Reading ==\n[https://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/txtSearch/rfid/List/1/ProductID/441/Default.aspx?SortField=ProductName%2cProductName Product Website]\n\n[https://www.parallax.com/Portals/0/Downloads/docs/prod/audiovis/28140-28340-RFID%20Reader-v2.1.pdf Data Sheet]\n\n[http://electronics.howstuffworks.com/rfid.htm How Stuff Works page on RFID]\n\n[http://en.wikipedia.org/wiki/RFID Wikipedia RFID page]\n\n[http://www.rfidnews.org/2008/05/30/understanding-rfid-part-9-rfid-privacy-and-security Privacy Concerns]"
                    }
                ]
            },
            "4212": {
                "pageid": 4212,
                "ns": 0,
                "title": "Real-Time Linux for TrackCam",
                "revisions": [
                    {
                        "contentformat": "text/x-wiki",
                        "contentmodel": "wikitext",
                        "*": "==Overview==\nProject by: James Yeung, Master in Electrical and Computer Engineering, 2010.<br>\nLast updated: June 11, 2010\n\nThe goal of this project was to install and set up an operating system with real-time capabilities to work with Photonfocus' TrackCam and SiliconSoftware's MicroEnable Frame Grabber. This is the continuation of [http://hades.mech.northwestern.edu/index.php/Control_with_TrackCam_Vision_Feedback_and_MATLAB earlier work] done with the same hardware but on Windows instead.\n\nSo what is a real-time operating system? A key characteristic of a real-time OS is the level of its consistency concerning the amount of time it takes to accept and complete an application's task; the variability is jitter. A hard real-time operating system has less jitter than a soft real-time operating system. The chief design goal is not high throughput, but rather a guarantee of a soft or hard performance category. A real-time OS that can usually or generally meet a deadline is a soft real-time OS, but if it can meet a deadline deterministically it is a hard real-time OS.\n\n==Implementations of Real-Time Operating Systems==\nThere are currently two main methods of implementing a real-time operating system, a micro-kernel approach and a scheduling approach. We will be using an operating system which implements the scheduling approach because it is the operating system that the makers of TrackCam supports.\n\n===Micro-Kernel===\nIn the micro-kernel approach, there's simply a very small, simple, real-time operating system underneath the main operating system. The main operating system becomes a task run only when there is no real-time task to run, and the micro-kernel will pre-empt the main operating system whenever a real-time task needs the processor. RTAI and RTLinux (not to be confused with the linux-rt patch) are examples of such implementation.\n\n===Scheduling===\nIn the scheduling approach, the operating system has a scheduling policy where when a task starts running, it continues to run until it voluntarily yields the processor, blocks or is preempted by a higher-priority real-time task. This is the first-in-first-out policy. Another common policy uses a timeslice model where tasks are allotted timeslices based on their priority and run until they exhaust their timeslice. The -rt Linux patch is an example of the scheduling implementation.\n\nThe scheduling in linux-rt has a total of 139 levels. The lower the level, the higher the priority it has. Level 100 to 139 maps to the -20 to 19 niceness levels. Nice is a program that allows you to manually set the priority of a particular process, but it only gives you access to the highest 40 levels. If you want access to the lower levels (higher priority levels), you will need to use the function \"sched_setscheduler\" in the \"sched.h\" library (see example code below).\n\n==Our Setup==\nSince SiliconSoftware provides drivers and support for the -rt Linux operating system, we will be using it for this project. Below is a list of hardware that we will be using.\n\n===Computer===\n* CPU - Intel Core 2 Quad Q8400 (2.66 GHz)\n* RAM - Crucial 4 GB DDR2 800 (PC2 6400)\n* Motherboard - Supermicro MBD-C2SBE-O\n* Hard Drive - 500 GB Seagate Barracude 7200.12, 7200 rpm\n* Video Card - EVGA 256-P2-N768-Fr GeForce 8600 GTS\n\n===Camera===\n* PhotonFocus MV-D1024-TrackCam\n* SiliconSoftware MicroEnable III Frame Grabber\n\n==How To Setup Linux-rt On openSUSE==\n===Overview===\nFor those who are not familiar with Linux, or how an operating system works, here is a quick run down on the basics. Linux is really just a kernel. A kernel is a piece of software that handles the interaction between hardware and applications. All the different kinds of \"Linux\" out there like Ubuntu, Fedora, openSUSE and Debian all have basically the same kernel, with a few tweaks here and there. The main difference between them is the GUI that is on top of the kernel, which gives them each their distinct look and feel. We will be using openSUSE because it has been tested by SiliconSoftware with their MicroEnable Frame Grabber.\n\n===Instructions===\n<ol>\n <li>Use an openSUSE Live CD to install a fresh copy of openSUSE.\n  <ul>\n   <li>In this guide, we will be using a 64-bit version.</li>\n   <li>Follow on screen instructions and note the root password that you set.</li>\n   <li>When prompted about partition setup, be sure to use file system format \u201cext3\u201d for the swap and home partitions. The version of the kernel that we will be building does not properly support \u201cext4\u201d (or higher).</li>\n  </ul>\n </li>\n\n <li>Check what is the newest version of the kernel that is supported by linux-rt and other applications/drivers that you will be using.\n  <ul>\n   <li>For linux-rt, you can check here: http://www.kernel.org/pub/linux/kernel/projects/rt/</li>\n   <li>In this guide, we will be using version 2.6.24.7, the highest kernel version that our frame grabber software was tested on.</li>\n  </ul>\n </li>\n\n <li>Download the vanilla kernel and the linux-rt patch\n  <ul>\n   <li>Open a terminal window. GNOME Terminal would do.</li>\n   <li>Go into superuser mode. You will need to know the root password.</li>\n >> su\n   <li>Go into the directory where source code is commonly stored.</li>\n >> cd /usr/src\n   <li>Download the vanilla kernel.</li>\n >> wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.24.7.tar.bz2\n   <li>Download the linux-rt patch for the matching kernel.</li>\n >> wget http://www.kernel.org/pub/linux/kernel/projects/rt/ older/patch-2.6.24.7-rt17.bz2\n  </ul>\n </li>\n\n <li>Unpack the packages</li>\n >> tar -xvjf linux-2.6.24.7.tar.bz2\n >> bunzip2 patch-2.6.24.7-rt17.bz2\n\n <li>Make symbolic link to new directory</li>\n >> rm -f linux\n >> ln -fs linux-2.6.24.7 linux\n\n <li>Copy the config file provided with the menable driver.</li>\n >> cd linux\n >> cp /home/lims/Download/menable/menable_linuxdrv_3.9.10/ 2.6.24.7-rt17/CORE2_x86_64/.config .config\n\n <li>Install needed packages</li>\n >> zypper in make patch gcc gtk2 gtk2-devel libglade2 libglade2-devel glib2 glib2-devel mkinitrd\n\n <li>Apply the linux-rt patch. (Note that p1 has a one, not L)</li>\n >> cat ../patch-2.6.29.6-rt24 | patch -p1\n\n <li>Make oldconfig</li>\n >> make oldconfig\n  <ul>\n   <li>When prompted, use default by pressing enter.</li>\n  </ul>\n </li>\n\n <li>Configure the config file through gconfig</li>\n >> make gconfig\n  <ul>\n   <li>Make sure the following sections are untouched:\n    <ol>\n     <li>Processor type and features</li>\n      <ul>\n       <li>Except you need to set \"High Resolution Timer Support\" to \"Yes\".</li>\n      </ul>\n     <li>Bus options</li>\n     <li>Kernel hacking</li>\n    </ol>\n   <li>If you know which modules are needed for your hardware, enable them.</li>\n   <li>If you don't know which modules are needed for your hardware, compile the kernel and error messages from your attempt to install will give you a better idea of which modules need to be enabled.</li>\n  </ul>\n\n <li>Save and close out of gconfig</li>\n\n <li>Need to change \u201cgetline\u201d to \u201cparseline\u201d in scripts/unifdef.c (There are 3 getline's)\n  <ul>\n   <li>Type \u201cvi scripts/unifdef.c\" to view and edit the file.</li>\n   <li>Type \u201c/getline\u201d to search for \u201cgetline\u201d</li>\n   <li>Hit \u201ci\u201d to get into insert mode</li>\n   <li>Change \u201cgetline\u201d to \u201cparseline\u201d</li>\n   <li>Hit the \u201cEscape\u201d key to get out of insert mode</li>\n   <li>Search again until all getlines are changed</li>\n   <li>Type \u201c:wq\u201d to save and quit</li>\n  </ul>\n </li>\n\n <li>Need to change \u201c=r\u201d to \u201c=q\u201d in arch/x86/boot/boot.h\n  <ul>\n   <li>Type \u201cvi arch/x86/boot/boot.h\u201d</li>\n   <li>Type \u201c112\u201d to get to line 112</li>\n   <li>Hit \u201ci\u201d to get into insert mode</li>\n   <li>Change \u201c=r\u201d to \u201c=q\u201d</li>\n   <li>Hit the \u201cEscape\u201d key to get out of insert mode</li>\n   <li>Type \u201c:wq\u201d to save and quit</li>\n  </ul>\n </li>\n\n <li>Compile & install kernel. (-j 4 is to use all 4 CPU cores, should take 15 minutes)</li>\n >> make -j 4\n >> make -j 4 modules_install\n >> make -j 4 install\n  <ul>\n   <li>If you didn't enable all required modules, you will get error messages hinting which ones you need here.</li>\n  </ul>\n\n <li>Make sure \u201cfstab\u201d has correct paths.</li>\n >> cd /etc\n >> vi fstab\n  <ul>\n   <li>The first couple of lines should look something like this:</li>\n /dev/sda5 swap       swap     defaults            0 0\n /dev/sda6 /          ext3     acl,user_xattr      1 1\n /dev/sda7 /home      ext3     acl,user_xattr      1 2\n   <li>If not, change the part after \u201c/dev/\u201d to \u201csda#\u201d where # is the corresponding number to \u201c-part#\u201d.</li>\n  </ul>\n\n <li>You'll need to change the grub config file to match the changes in fstab.\n  <ul>\n   <li>Grub is the software that controls which OS to boot into during boot up.</li>\n   <li>Files to configure Grub are located at /boot/grub/</li>\n   <li>You may also want to edit the menu.lst file to your desire.</li>\n  </ul>\n </li>\n</ol>\n\n==How To Install MicroEnable Frame Grabber Drivers/Software==\n<ul>\n <li>This guide assumes that you have downloaded and untarred the following files under /home/lims/Download/menable/\n  <ul>\n   <li>menable_linuxdrv_3.9.10.tar.bz2</li>\n   <li>siso-rt3-meIII-3.2.1-2.i586.rpm</li>\n   <li>siso-rt-basesystem-1.0.0-1.i586.rpm</li>\n  </ul>\n </li>\n <li>We will be using menable_linuxdrv_3.9.10 with the 2.6.23.7-rt17 kernel.</li>\n</ul>\n\n===Drivers===\n<ol>\n <li>Go into the root of the driver folder.</li>\n >> cd /home/lims/Download/menable/menable_linuxdrv_3.9.10\n <li>We need to copy the binary objects from the subdirectory matching our kernel and architecture.</li>\n >> cp 2.6.24.7-rt17/CORE2_x86_64/* .\n <li>Compile</li>\n >> ./compile.sh\n <li>Install the compiled driver</li>\n >> insmod menable.ko\n <li>Confirm the install</li>\n >> dmesg | tail\n  <ul>\n   <li>The output should look like this:</li>\n 0000:00:19.0: eth0: Link is Up 100 Mbps Full Duplex, Flow Control: RX\n 0000:00:19.0: eth0: 10/100 speed: disabling TSO\n ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready\n martian source 255.255.255.255 from 129.105.69.13, on dev eth0\n ll header: ff:ff:ff:ff:ff:ff:d4:9a:20:d0:13:88:08:00\n eth0: no IPv6 routers present\n devkit-disks-da[3482]: segfault at 18 rip 41cb5e rsp 7fff78425850 error 4\n ACPI: PCI Interrupt 0000:11:00.0[A] -> GSI 20 (level, low) -> IRQ 20\n menable 0000:11:00.0: microEnable III card will be called menable0\n menable menable0: allocated dummy DMA area of 128 kiB\n  </ul>\n </li>\n</ol>\n\n===Software===\n<ol>\n <li>Go into the direction with the RPM packages.</li>\n >> cd /home/lims/Download/menable\n\n <li>Install the two rpm packages.</li>\n >> rpm -i siso-rt-basesystem-1.0.0-1.i586.rpm\n >> rpm -i siso-rt3-meIII-3.2.1-2.i586.rpm\n\n <li>Confirm the install\n  <ul>\n   <li>Use the following commands to see where the files should have been installed.</li>\n >> rpm -qlp siso-rt-basesystem-1.0.0-1.i586.rpm\n >> rpm -qlp siso-rt3-meIII-3.2.1-2.i586.rpm\n   <li>Go into the directories and see if they are there.</li>\n  </ul>\n </li>\n</ol>\n\n==How To Install OpenCV on Linux==\n<ol>\n <li>Make sure the following packages are installed.\n  <ul>\n   <li>gcc (version 4.x)</li>\n   <li>cmake (version 2.6 or higher)</li>\n   <li>pkg-config</li>\n  </ul>\n </li>\n <li>Download module \u201cFindOpenCV.cmake\u201d and add it to cmake.\n  <ul>\n   <li>Module can be downloaded via: http://opencv.willowgarage.com/wiki/Getting_started?action=AttachFile&do=view&target=FindOpenCV.cmake</li>\n   <li>Download and place file into /usr/share/cmake/Modules.</li>\n  </ul>\n </li>\n <li>Download OpenCV source code, configure, compile and install.\n  <ul>\n   <li>Source can be downloaded via: http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/2.0/</li>\n   <li>./configure</li>\n   <li>make</li>\n   <li>make install</li>\n  </ul>\n </li>\n <li>(Optional) Go through the \u201cHello World\u201d tutorial to make sure it works.\n  <ul>\n   <li>http://opencv.willowgarage.com/wiki/Getting_started</li>\n  </ul>\n </li>\n</ol>\n\n==Benchmarking==\nThis section was an attempt to see how \"real-time\" the operating system is.\n\n===Cyclictest===\nCyclictest is program available on the rt-wiki for testing the latency of commands on your operating system. All the code and instructions are available on their website.\n\nhttp://rt.wiki.kernel.org/index.php/Cyclictest\n\n===Homemade Test===\nI found a \"Hello World\" code for real-time applications on the -rt Linux patch online. I then modified the code such that it would have the highest possible priority (1) and that it would take a snapshot of the timer every 10ms.\n\nTo compile: g++ main.cpp -o main -lrt \n\n<pre>\n#include <stdlib.h>\n#include <stdio.h>\n#include <time.h>\n#include <sys/time.h>\n#include <sched.h>\n#include <sys/mman.h>\n#include <string.h>\n\n#define MY_PRIORITY (1 /* we use 1 as the PRREMPT_RT use 50\n                            as the priority of kernel tasklets\n                            and interrupt handler by default */\n\n#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is\n                                   guranteed safe to access without\n                                   faulting */\n\n#define NSEC_PER_SEC (1000000000) /* The number of nsecs per sec. */\n\nvoid stack_prefault(void) {\n    unsigned char dummy[MAX_SAFE_STACK];\n    memset(&dummy, 0, MAX_SAFE_STACK);\n    return;\n}\n\nint main(int argc, char* argv[]) {\n\n    //int time[1002];\n    //int rc[1002];\n\n    struct timespec t;\n    struct timespec ti[1002];\n    struct timespec tt[1002];\n    struct timespec tf[1002];\n    struct sched_param param;\n    int interval = 10000000; /* 10ms*/\n\n    /* Declare ourself as a real time task */\n\n    param.sched_priority = MY_PRIORITY;\n    if(sched_setscheduler(0, SCHED_FIFO, &param) == -1) {\n        perror(\"sched_setscheduler failed\");\n        exit(-1);\n    }\n\n    /* Lock memory */\n \n    if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {\n        perror(\"mlockall failed\");\n        exit(-2);\n    }\n\n    /* Pre-fault our stack */\n \n    stack_prefault();\n\n    clock_gettime(CLOCK_MONOTONIC ,&t);\n    /* start after one second */\n    t.tv_sec++;\n \n    int ii = 0;\n    while(ii < 1002) {\n    \n        tt[ii].tv_nsec = t.tv_nsec; //target time stamp\n        clock_gettime(CLOCK_MONOTONIC ,&ti[ii]); //time before releasing CPU\n\n        /* release CPU until target time stamp is reached */\n        clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);\n        \n        /* do the stuff */\n        clock_gettime(CLOCK_MONOTONIC ,&tf[ii]); //time right after waking up\n        ii++;\n\n        /* calculate next target */\n        t.tv_nsec += interval;\n        while (t.tv_nsec >= NSEC_PER_SEC) {\n            t.tv_nsec -= NSEC_PER_SEC;\n            t.tv_sec++;\n        }\n    }\n    for(int ii = 1; ii < 1002; ii++){\n        printf(\"%d %d %d %d\\t%d\\n\", ti[ii].tv_nsec, tt[ii].tv_nsec, tf[ii].tv_nsec, tf[ii].tv_nsec - tt[ii].tv_nsec, tf[ii].tv_nsec - tf[ii-1].tv_nsec);\n    }\n}\n</pre>\n\n===PThread Test===\nThis is a test that uses multiple threads where only one is set with a high priority and the rest are normal priority. The high priority thread is also reading one analog signal from a data acquisition card and writing one analog signal to an output card. In this particular test I had four normal priority threads, each continuously performing of the the basic simple math operation (+, -, *, /). This was then tested on a system with a quad core CPU. The four normal priority threads is to ensure that there are more than four active tasks for the CPU to handle. In a true setup, a total of two threads may be enough for your needs. More details about the cards used in this test can be found [http://hades.mech.northwestern.edu/index.php/NI-DAQ_Cards_on_Linux here].<br>\n<br>\nThe output of the results will be in the form:<br>\ninterval = nanoseconds per interrupt<br>\nduration = number of times to trigger<br>\nstarttime = system time of first trigger<br>\nmaxDelay = maximum delay in microseconds experienced in this setup (>99 shows up as 99)<br>\nbins[x] = number of occurrences with x microseconds of delay in this setup<br>\nearlytime[for the xth trigger] = actual wakeup time (t = target wakeup time) value = microseconds of delay<br>\nbadtime[xth >30us delay] = actual wakeup time (t = target wakeup time) value = microseconds of delay in this setup<br>\n<br>\nYou can download a sample of results [http://hades.mech.northwestern.edu/images/b/b9/RTLinux-PThreadTest-SampleResults.zip here].<br>\n<br>\nTo compile: gcc main.c -o main -lrt -lnidaqmx\n\n<pre>\n#define _REENTRANT\n#define NSEC_PER_SEC (1000000000) /* The number of nsecs per sec. */\n#define MY_PRIORITY (2) /* we use 49 as the PRREMPT_RT use 50\n\t\t\tas the priority of kernel tasklets\n\t\t\tand interrupt handler by default */\n#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is\n\t\t\t\tguranteed safe to access without\n\t\t\t\tfaulting */\n#define DAQmxErrChk(functionCall) if(DAQmxFailed(error=functionCall)) goto Error; else\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <sys/time.h>\n#include <sched.h>\n#include <sys/mman.h>\n#include <string.h>\n#include <NIDAQmx.h>\n\n/* function prototypes */\nvoid* rtTask1( );\nvoid* Task2( );\nvoid* Task3( );\nvoid* Task4( );\nvoid* Task5( );\n\nvoid stack_prefault(void) {\n\tunsigned char dummy[MAX_SAFE_STACK];\n\tmemset(&dummy, 0, MAX_SAFE_STACK);\n\treturn;\n}\n\n/* mutex variables */\npthread_mutex_t printfLock = PTHREAD_MUTEX_INITIALIZER;\n\nint end2 = 0;\nint end3 = 0;\nint end4 = 0;\nint end5 = 0;\ndouble temp = 0;\n\nint interval = 100000;\t\t//100us \"interrupt\" interval\nint duration = 10000*60*60*1;\t//10khz for 1 hours\n\n/* global variables for input/output */\nTaskHandle inputTaskHandle=0;\nTaskHandle outputTaskHandle=0;\nint32 error=0;\nint32 read;\nfloat64 data;\nchar errBuff[2048]={'\\0'};\n\nint main( void ){\n\tpthread_t thr1, thr2, thr3, thr4, thr5;\n\tstruct timespec t[14];\n\n\t/*********************************************/\n\t// DAQmx Configure Code\n\t/*********************************************/\n\tDAQmxErrChk(DAQmxCreateTask(\"AnalogIn\",&inputTaskHandle));\n\tDAQmxErrChk(DAQmxCreateTask(\"AnalogOut\",&outputTaskHandle));\n\tDAQmxErrChk(DAQmxCreateAIVoltageChan(inputTaskHandle,\"AnalogIn/ai0\",\"TestChannel\",DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NULL));\n\tDAQmxErrChk(DAQmxCreateAOVoltageChan(outputTaskHandle,\"AnalogOut/ao6\",\"\",-10.0,10.0,DAQmx_Val_Volts,\"\"));\n\n\t/*********************************************/\n\t// DAQmx Start Code\n\t/*********************************************/\n\tDAQmxErrChk(DAQmxStartTask(inputTaskHandle));\n\tDAQmxErrChk(DAQmxStartTask(outputTaskHandle));\n\n\tclock_gettime(CLOCK_MONOTONIC ,&t[0]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[1]);\n\tpthread_create( &thr2, NULL, Task2, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[2]);\n\tpthread_create( &thr3, NULL, Task3, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[3]);\n\tpthread_create( &thr4, NULL, Task4, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[4]);\n\tpthread_create( &thr5, NULL, Task5, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[5]);\n\n\tpthread_join( thr1, NULL );\n\tinterval = 100000;\t\t//100us \"interrupt\" interval\n\tduration = 10000*60*60*5;\t//10khz for 5 hours\n\tclock_gettime(CLOCK_MONOTONIC ,&t[6]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[7]);\n\n\tpthread_join( thr1, NULL );\n\tinterval = 200000;\t\t//200us \"interrupt\" interval\n\tduration = 5000*60*60*5;\t//5khz for 5 hours\n\tclock_gettime(CLOCK_MONOTONIC ,&t[8]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[9]);\n\n\tpthread_join( thr1, NULL );\n\tinterval = 500000;\t\t//500us \"interrupt\" interval\n\tduration = 2000*60*60*5;\t//2khz for 5 hours\n\tclock_gettime(CLOCK_MONOTONIC ,&t[10]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[11]);\n\n\tpthread_join( thr1, NULL );\n\tinterval = 1000000;\t\t//1ms \"interrupt\" interval\n\tduration = 1000*60*60*5;\t//1khz for 5 hours\n\tclock_gettime(CLOCK_MONOTONIC ,&t[12]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[13]);\n\n\tpthread_join( thr1, NULL );\n\n\tend2 = 1;\n\tend3 = 1;\n\tend4 = 1;\n\tend5 = 1;\n\n\tpthread_join( thr2, NULL );\n\tpthread_join( thr3, NULL );\n\tpthread_join( thr4, NULL );\n\tpthread_join( thr5, NULL );\n\n\tFILE *file;\n\tfile = fopen(\"results\",\"a+\");\n\n\tint ii;\n\tfor(ii = 1; ii < 14; ii++){\n\t\tfprintf(file, \"Task%d %9d %9d\\n\", ii, t[ii].tv_nsec, t[ii].tv_nsec - t[ii-1].tv_nsec);\n\t}\n\tfprintf(file, \"\\n========================================\\n\");\n\n\treturn 0;\n\nError:\n\tif( DAQmxFailed(error) ){\n\t\tDAQmxGetExtendedErrorInfo(errBuff,2048);\n\t}\n\tif( inputTaskHandle!=0 ){\n\t\tDAQmxStopTask(inputTaskHandle);\n\t\tDAQmxClearTask(inputTaskHandle);\n\t}\n\tif( outputTaskHandle!=0){\n\t\tDAQmxStopTask(outputTaskHandle);\n\t\tDAQmxClearTask(outputTaskHandle);\n\t}\n\tif( DAQmxFailed(error) ){\n\t\tprintf(\"DAQmx Error: %s\\n\",errBuff);\n\t}\n\treturn 0;\n}\n\nvoid* rtTask1(){\n\n\tint bins[100];\n\tint i, ii, jj;\n\tfor( i = 0; i < 100; i++){\n\t\tbins[i] = 0;\n\t}\n\tstruct timespec t;\n\tstruct timespec timestamp;\n\tstruct timespec starttime;\n\tstruct timespec earlytime[100];\n\tstruct timespec earlytime2[100];\n\tint earlyvalue[100];\n\tstruct timespec badtime[10000];\n\tstruct timespec badtime2[10000];\n\tint badvalue[10000];\n\tstruct sched_param param;\n\n\tFILE *file;\n\tfile = fopen(\"results\",\"a+\");\n\n\t/* Declare ourself as a real time task */\n\tparam.sched_priority = MY_PRIORITY;\n\tif(sched_setscheduler(0, SCHED_FIFO, &param) == -1) {\n\t\tperror(\"sched_setscheduler failed\");\n\t\texit(-1);\n\t}\n\n\t/* Lock memory */\n\tif(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {\n\t\tperror(\"mlockall failed\");\n\t\texit(-2);\n\t}\n\n\t/* Pre-fault our stack */\n\tstack_prefault();\n\n\tclock_gettime(CLOCK_MONOTONIC,&t);\n\t/* start after one second */\n\tt.tv_sec++;\n\tstarttime = t;\n\tjj = 0;\n\n\tfor( i = 0; i < duration; ++i ) {\n\t\t/* wait until next shot */\n\t\tclock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);\n\t\tclock_gettime(CLOCK_MONOTONIC ,&timestamp);\n\t\tif(timestamp.tv_sec == t.tv_sec){\n\t\t\tii = (int)(((timestamp.tv_nsec - t.tv_nsec)/1000) - 1);\n\t\t}else{\n\t\t\tii = (int)((((timestamp.tv_sec - t.tv_sec) * NSEC_PER_SEC - t.tv_nsec + timestamp.tv_nsec)/1000) - 1);\n\t\t}\n\t\t// store timestamps if latency > 30\n\t\tif(ii > 30){\n\t\t\tif(jj < 10000){\n\t\t\t\tbadtime[jj] = timestamp;\n\t\t\t\tbadtime2[jj] = t;\n\t\t\t\tbadvalue[jj] = ii;\n\t\t\t\tjj++;\n\t\t\t}\n\t\t}\n\t\t// store timestamps for first 100 wakeups\n\t\tif(i < 100){\n\t\t\tearlytime[i] = timestamp;\n\t\t\tearlytime2[i] = t;\n\t\t\tearlyvalue[i] = ii;\n\t\t}\n\t\t// cap delay for bins[ii]\n\t\tif(ii > 99){\n\t\t\tii = 99;\n\t\t}else if(ii < 0){\n\t\t\tii = 0;\n\t\t}\n\t\tbins[ii]++;\n\n\t\tDAQmxErrChk(DAQmxReadAnalogScalarF64(inputTaskHandle,10.0,&data,NULL));\n\t\tDAQmxErrChk(DAQmxWriteAnalogScalarF64(outputTaskHandle,1,10.0,data,NULL));\n\n\t\tt.tv_nsec += interval;\n\t\twhile (t.tv_nsec >= NSEC_PER_SEC) {\n\t\t\tt.tv_nsec -= NSEC_PER_SEC;\n\t\t\tt.tv_sec++;\n\t\t}\n\t}\n\n\tfprintf(file, \"interval = %d\\n\", interval);\n\tfprintf(file, \"duration = %d\\n\", duration);\n\tfprintf(file, \"starttime = %ds\\t%dns\\n\", starttime.tv_sec, starttime.tv_nsec);\n\tint maxDelay = 0;\n\tfor( i = 0; i < 100; i++){\n\t\tif( bins[i] > 0 ){\n\t\t\tmaxDelay = i;\n\t\t}\n\t}\n\tfprintf(file, \"maxDelay = %d\\n\", maxDelay);\n\tfor( i = 0; i < 100; i++){\n\t\tfprintf(file, \"bins[%d] = %d\\n\", i, bins[i]);\n\t}\n\tfor( i = 0; i < 100; i++){\n\t\tfprintf(file, \"earlytime[%d] = %ds\\t%dns (t = %ds\\t%dns)\\tvalue = %d\\n\", i, earlytime[i].tv_sec, earlytime[i].tv_nsec, earlytime2[i].tv_sec, earlytime2[i].tv_nsec, earlyvalue[i]);\n\t}\n\tfor( i = 0; i < jj; i++){\n\t\tfprintf(file, \"badtime[%d] = %ds\\t%dns (t = %ds\\t%dns)\\tvalue = %d\\n\", i, badtime[i].tv_sec, badtime[i].tv_nsec, badtime2[i].tv_sec, badtime2[i].tv_nsec, badvalue[i]);\n\t}\n\tfprintf(file, \"\\n\");\n\tfclose(file);\n\treturn NULL;\n\nError:\n\tif( DAQmxFailed(error) ){\n\t\tDAQmxGetExtendedErrorInfo(errBuff,2048);\n\t}\n\tif( inputTaskHandle!=0 ){\n\t\tDAQmxStopTask(inputTaskHandle);\n\t\tDAQmxClearTask(inputTaskHandle);\n\t}\n\tif( outputTaskHandle!=0){\n\t\tDAQmxStopTask(outputTaskHandle);\n\t\tDAQmxClearTask(outputTaskHandle);\n\t}\n\tif( DAQmxFailed(error) ){\n\t\tprintf(\"DAQmx Error: %s\\n\",errBuff);\n\t}\n\treturn NULL;\n}\n\nvoid* Task2(){\n\twhile( end2 == 0 ) {\n\t\ttemp = temp + rand();\n\t}\n\treturn NULL;\n}\n\nvoid* Task3(){\n\twhile( end3 == 0 ) {\n\t\ttemp = temp - rand();\n\t}\n\treturn NULL;\n}\n\nvoid* Task4(){\n\twhile( end4 == 0 ) {\n\t\ttemp = temp * rand();\n\t}\n\treturn NULL;\n}\n\nvoid* Task5(){\n\twhile( end4 == 0 ) {\n\t\ttemp = temp / rand();\n\t}\n\treturn NULL;\n}\n</pre>\n\n==Results==\nTo ensure that the operating system is truly \"real-time\", I ran the two tests (Cyclictest and Homemade Test) under two conditions, load and no load. The no load condition is simply where except for system processes, only the test programs are running. The load condition is where I compile the source code of OpenCV and the Linux kernel using a niceness of -20.\n\nAs for the PThread Test, it was run under the no load condition.\n\n===Cyclictest===\nUsing the Cyclictest program, I was able to get some very nice results.\n\nArguments:<br>\nt = number of threads to work<br>\np = priority level to set<br>\nn = use nano_sleep<br>\ni = interval in microseconds<br>\nl = number of times to loop<br>\n<br>\nOutputs:<br>\nT = ?<br>\nP = priority level<br>\nI = interval in microseconds<br>\nC = count (number of tasks performed, this number counts up during the test)<br>\nMin = minimum latency experienced<br>\nAct = current latency (this number changes during the test)<br>\nAvg = average latency experienced<br>\nMax = maximum latency experienced<br>\n\n<ul>\n <li>No Load\n  <ul>\n   <li>Priority Level = 1</li>\n sudo cyclictest -t1 -p 1 -n -i 1000 -l 10000\n policy: fifo: loadavg: 1.14 0.46 0.18 1/291 5891          \n T: 0 ( 5791) P: 1 I:1000 C:  10000 Min:      1 Act:    1 Avg:    1 Max:     712\n   <li>Priority Level = 2</li>\n sudo cyclictest -t1 -p 2 -n -i 1000 -l 10000\n policy: fifo: loadavg: 0.11 0.59 0.44 1/294 16748          \n T: 0 (16624) P: 2 I:1000 C:  10000 Min:      1 Act:    2 Avg:    1 Max:       5\n   <li>Priority Level = 80</li>\n sudo cyclictest -t1 -p 80 -n -i 1000 -l 10000\n policy: fifo: loadavg: 0.19 0.66 0.46 1/293 16414           \n T: 0 (16289) P:80 I:1000 C:  10000 Min:      2 Act:    2 Avg:    2 Max:       8\n  </ul>\n </li>\n <li>Under Load\n  <ul>\n   <li>Priority Level = 1</li>\n sudo cyclictest -t1 -p 1 -n -i 1000 -l 10000\n policy: fifo: loadavg: 3.52 1.16 0.44 16/331 11374          \n T: 0 ( 8812) P: 1 I:1000 C:  10000 Min:      1 Act:    3 Avg:   10 Max:    3093\n   <li>Priority Level = 2</li>\n sudo cyclictest -t1 -p 2 -n -i 1000 -l 10000\n policy: fifo: loadavg: 4.56 1.47 0.74 14/333 23801          \n T: 0 (22748) P: 2 I:1000 C:  10000 Min:      1 Act:    5 Avg:    6 Max:      17\n   <li>Priority Level = 80</li>\n sudo cyclictest -t1 -p 80 -n -i 1000 -l 10000\n policy: fifo: loadavg: 5.40 2.38 1.15 12/332 28779          \n T: 0 (27402) P:80 I:1000 C:  10000 Min:      1 Act:    6 Avg:    5 Max:      14\n  </ul>\n </li>\n</ul>\nThe most important number to note in the results is the max because in order to assure hard real-time performance, the system should never have a latency greater than a certain threshold. That threshold would be determined by the frequency of the task that you are trying to perform. For example, if you wanted to perform a task at 100kHz and the worst case latency is 5us, then your task should take no more than 5us since there are only 10us between each consecutive tasks.<br>\n<br>\nIt is interesting to note that when using a priority level of 1 in Cyclictest, we get a much higher max than a priority level of 2 or 80. I still don't have a reasoning for this, but this phenomenon is also apparent in the Homemade Test and PThread Test.\n\n===Homemade Test===\nThe results of this test can be summarized and better visualized in histograms. Below are two histograms from each of the two conditions. It is important to note that these tests were performed with priority level set at 1. The PThread test is a better measure of how real-time the system is.\n\n[[image:RTLinux_Homemade_Latency_Noload.JPG|thumb|500px|Latencies between target time stamp and recorded time stamp under no-load conditions.|left]]\n[[image:RTLinux_Homemade_Interval_Noload.JPG|thumb|500px|Intervals between consecutive time stamps under no-load conditions.|right]]\n[[image:RTLinux_Homemade_Latency_Underload.JPG|thumb|500px|Latencies between target time stamp and recorded time stamp under load conditions.|left]]\n[[image:RTLinux_Homemade_Interval_Underload.JPG|thumb|500px|Intervals between consecutive time stamps under load conditions.|right]]\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.\n\n===PThread Test===\nThe PThread was run on four different frequencies, 1kHz, 2kHz, 5kHz and 10kHz. Each frequency was tested for 5 hours. I have decided to run the test with the priority level set at 2 due to the unexplained phenomenon of a higher max latency at priority level 1 experienced in the Cyclictest. The graphs below show a histogram of latencies experienced in microseconds. I only kept count of latencies of up to 100us because a latency of 100us or greater would be considered catastrophic. Furthermore, the maximum latency experienced at the start of the test are generally high, so I have decided to discard the first hour as warm up time for the system. However, a one second warm up will be enough in practice since the max latency is experienced within the first one millisecond. The results from test are shown below.\n\n[[image:RTLinux1kHz.jpg|thumb|500px|Linux-rt delay from wakeup target time at 1kHz.|left]]\n[[image:RTLinux2kHz.jpg|thumb|500px|Linux-rt delay from wakeup target time at 2kHz.|right]]\n[[image:RTLinux5khz.jpg|thumb|500px|Linux-rt delay from wakeup target time at 5kHz.|left]]\n[[image:RTLinux10khz.jpg|thumb|500px|Linux-rt delay from wakeup target time at 10kHz.|right]]\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.\n\n==Plotting Data in Real-Time==\nHere's a version of the PThread Test code that reads the encoder counts at 10kHz for 1 minute and then 5kHz at 1 minute. While it is reading, it will output motor angle at 10Hz. If you pipe this into the driveGnuPlotStream.pl script, it will plot the motor angle in real-time at 10Hz.<br>\n<br>\nClick [http://www.lysium.de/blog/index.php?/archives/234-Plotting-data-with-gnuplot-in-real-time.html here] to download or for more details about the driveGnuPlotStream.pl script.<br>\n<br>\nTo compile: gcc main.c -o main -lrt -lnidaqmx<br>\nTo run:\n sudo ./main | perl ./driveGnuPlotStreams.pl 1 1 50 25000 45000 500x300+0+0 'MotorAngle' 0\nNote: You will need to have the script in the same directory.\n<pre>\n#define _REENTRANT\n#define NSEC_PER_SEC (1000000000) /* The number of nsecs per sec. */\n#define MY_PRIORITY (2) /* we use 49 as the PRREMPT_RT use 50\n\t\t\tas the priority of kernel tasklets\n\t\t\tand interrupt handler by default */\n#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is\n\t\t\t\tguranteed safe to access without\n\t\t\t\tfaulting */\n#define DAQmxErrChk(functionCall) if(DAQmxFailed(error=functionCall)) goto Error; else\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <sys/time.h>\n#include <sched.h>\n#include <sys/mman.h>\n#include <string.h>\n#include <NIDAQmx.h>\n#include <math.h>\n\n/* function prototypes */\nvoid* rtTask1( );\nvoid* Task2( );\nvoid* Task3( );\nvoid* Task4( );\nvoid* Task5( );\n\nvoid stack_prefault(void) {\n\tunsigned char dummy[MAX_SAFE_STACK];\n\tmemset(&dummy, 0, MAX_SAFE_STACK);\n\treturn;\n}\n\n/* mutex variables */\npthread_mutex_t printfLock = PTHREAD_MUTEX_INITIALIZER;\n\nint end2 = 0;\nint end3 = 0;\nint end4 = 0;\nint end5 = 0;\nint aa = 0;\ndouble temp = 0;\n\nint interval = 100000;\t\t//100us \"interrupt\" interval\nint duration = 10000;\t\t//10khz for 1 second\n\n/* global variables for input/output */\nTaskHandle encoderTaskHandle=0;\nint32 error=0;\nint32 read;\nfloat64 motorAngle;\nchar errBuff[2048]={'\\0'};\n\nint main( void ){\n\tpthread_t thr1, thr2, thr3, thr4, thr5;\n\tstruct timespec t[10];\n\n\t/*********************************************/\n\t// DAQmx Configure Code\n\t/*********************************************/\n\tDAQmxErrChk(DAQmxCreateTask(\"EncoderTask\",&encoderTaskHandle));\n\tDAQmxErrChk(DAQmxCreateCIAngEncoderChan(encoderTaskHandle,\"AnalogIn/ctr0\",\"Counter\",DAQmx_Val_X4,0,0.0,DAQmx_Val_AHighBHigh,DAQmx_Val_Degrees,600,36000.0,\"\"));\n\n\t/*********************************************/\n\t// DAQmx Start Code\n\t/*********************************************/\n\tDAQmxErrChk(DAQmxStartTask(encoderTaskHandle));\n\n\tclock_gettime(CLOCK_MONOTONIC ,&t[0]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[1]);\n\tpthread_create( &thr2, NULL, Task2, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[2]);\n\tpthread_create( &thr3, NULL, Task3, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[3]);\n\tpthread_create( &thr4, NULL, Task4, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[4]);\n\tpthread_create( &thr5, NULL, Task5, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[5]);\n\n\tpthread_join( thr1, NULL );\n\tinterval = 100000;\t\t//100us \"interrupt\" interval\n\tduration = 10000*60;\t\t//10khz for 1 minute\n\tclock_gettime(CLOCK_MONOTONIC ,&t[6]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[7]);\n\n\tpthread_join( thr1, NULL );\n\tinterval = 200000;\t\t//200us \"interrupt\" interval\n\tduration = 5000*60;\t\t//5khz for 1 minute\n\tclock_gettime(CLOCK_MONOTONIC ,&t[8]);\n\tpthread_create( &thr1, NULL, rtTask1, NULL );\n\tclock_gettime(CLOCK_MONOTONIC ,&t[9]);\n\n\tpthread_join( thr1, NULL );\n\n\tend2 = 1;\n\tend3 = 1;\n\tend4 = 1;\n\tend5 = 1;\n\n\tpthread_join( thr2, NULL );\n\tpthread_join( thr3, NULL );\n\tpthread_join( thr4, NULL );\n\tpthread_join( thr5, NULL );\n\n\tFILE *file;\n\tfile = fopen(\"encodertiming\",\"a+\");\n\n\tint ii;\n\tfor(ii = 1; ii < 10; ii++){\n\t\tfprintf(file, \"Task%d %9d %9d\\n\", ii, t[ii].tv_nsec, t[ii].tv_nsec - t[ii-1].tv_nsec);\n\t}\n\tfprintf(file, \"\\n========================================\\n\");\n\n\treturn 0;\n\nError:\n\tif( DAQmxFailed(error) ){\n\t\tDAQmxGetExtendedErrorInfo(errBuff,2048);\n\t}\n\tif( encoderTaskHandle!=0 ){\n\t\tDAQmxStopTask(encoderTaskHandle);\n\t\tDAQmxClearTask(encoderTaskHandle);\n\t}\n\tif( DAQmxFailed(error) ){\n\t\tprintf(\"DAQmx Error: %s\\n\",errBuff);\n\t}\n\treturn 0;\n}\n\nvoid* rtTask1(){\n\n\tint bins[100];\n\tint i, ii, jj;\n\tfor( i = 0; i < 100; i++){\n\t\tbins[i] = 0;\n\t}\n\tstruct timespec t;\n\tstruct timespec timestamp;\n\tstruct timespec starttime;\n\tstruct timespec earlytime[100];\n\tstruct timespec earlytime2[100];\n\tint earlyvalue[100];\n\tstruct timespec badtime[10000];\n\tstruct timespec badtime2[10000];\n\tint badvalue[10000];\n\tstruct sched_param param;\n\n\tFILE *file;\n\tfile = fopen(\"encodertiming\",\"a+\");\n\tint plotInterval = NSEC_PER_SEC / interval / 10;\n\n\t/* Declare ourself as a real time task */\n\tparam.sched_priority = MY_PRIORITY;\n\tif(sched_setscheduler(0, SCHED_FIFO, &param) == -1) {\n\t\tperror(\"sched_setscheduler failed\");\n\t\texit(-1);\n\t}\n\n\t/* Lock memory */\n\tif(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {\n\t\tperror(\"mlockall failed\");\n\t\texit(-2);\n\t}\n\n\t/* Pre-fault our stack */\n\tstack_prefault();\n\n\tclock_gettime(CLOCK_MONOTONIC,&t);\n\t/* start after one second */\n\tt.tv_sec++;\n\tstarttime = t;\n\tjj = 0;\n\n\tfor( i = 0; i < duration; ++i ) {\n\t\t/* wait until next shot */\n\t\tclock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);\n\t\tclock_gettime(CLOCK_MONOTONIC ,&timestamp);\n\t\tif(timestamp.tv_sec == t.tv_sec){\n\t\t\tii = (int)(((timestamp.tv_nsec - t.tv_nsec)/1000) - 1);\n\t\t}else{\n\t\t\tii = (int)((((timestamp.tv_sec - t.tv_sec) * NSEC_PER_SEC - t.tv_nsec + timestamp.tv_nsec)/1000) - 1);\n\t\t}\n\t\t// store timestamps if latency > 30\n\t\tif(ii > 30){\n\t\t\tif(jj < 10000){\n\t\t\t\tbadtime[jj] = timestamp;\n\t\t\t\tbadtime2[jj] = t;\n\t\t\t\tbadvalue[jj] = ii;\n\t\t\t\tjj++;\n\t\t\t}\n\t\t}\n\t\t// store timestamps for first 100 wakeups\n\t\tif(i < 100){\n\t\t\tearlytime[i] = timestamp;\n\t\t\tearlytime2[i] = t;\n\t\t\tearlyvalue[i] = ii;\n\t\t}\n\t\t// cap delay for bins[ii]\n\t\tif(ii > 99){\n\t\t\tii = 99;\n\t\t}else if(ii < 0){\n\t\t\tii = 0;\n\t\t}\n\t\tbins[ii]++;\n\n\t\tDAQmxErrChk(DAQmxReadCounterF64(encoderTaskHandle,1,10.0,&motorAngle,1,&read,NULL));\n\n\t\tif(i % plotInterval == 0){\n\t\t\tprintf(\"0:%f\\n\",motorAngle);\n\t\t\tfflush(stdout);\n\t\t}\n\n\t\tt.tv_nsec += interval;\n\t\twhile (t.tv_nsec >= NSEC_PER_SEC) {\n\t\t\tt.tv_nsec -= NSEC_PER_SEC;\n\t\t\tt.tv_sec++;\n\t\t}\n\t}\n\n\tfprintf(file, \"interval = %d\\n\", interval);\n\tfprintf(file, \"duration = %d\\n\", duration);\n\tfprintf(file, \"starttime = %ds\\t%dns\\n\", starttime.tv_sec, starttime.tv_nsec);\n\tint maxDelay = 0;\n\tfor( i = 0; i < 100; i++){\n\t\tif( bins[i] > 0 ){\n\t\t\tmaxDelay = i;\n\t\t}\n\t}\n\tfprintf(file, \"maxDelay = %d\\n\", maxDelay);\n\tfor( i = 0; i < 100; i++){\n\t\tfprintf(file, \"bins[%d] = %d\\n\", i, bins[i]);\n\t}\n\tfor( i = 0; i < 100; i++){\n\t\tfprintf(file, \"earlytime[%d] = %ds\\t%dns (t = %ds\\t%dns)\\tvalue = %d\\n\", i, earlytime[i].tv_sec, earlytime[i].tv_nsec, earlytime2[i].tv_sec, earlytime2[i].tv_nsec, earlyvalue[i]);\n\t}\n\tfor( i = 0; i < jj; i++){\n\t\tfprintf(file, \"badtime[%d] = %ds\\t%dns (t = %ds\\t%dns)\\tvalue = %d\\n\", i, badtime[i].tv_sec, badtime[i].tv_nsec, badtime2[i].tv_sec, badtime2[i].tv_nsec, badvalue[i]);\n\t}\n\tfprintf(file, \"\\n\");\n\tfclose(file);\n\treturn NULL;\n\nError:\n\tif( DAQmxFailed(error) ){\n\t\tDAQmxGetExtendedErrorInfo(errBuff,2048);\n\t}\n\tif( encoderTaskHandle!=0 ){\n\t\tDAQmxStopTask(encoderTaskHandle);\n\t\tDAQmxClearTask(encoderTaskHandle);\n\t}\n\tif( DAQmxFailed(error) ){\n\t\tprintf(\"DAQmx Error: %s\\n\",errBuff);\n\t}\n\treturn NULL;\n}\n\nvoid* Task2(){\n\twhile( end2 == 0 ) {\n\t\ttemp = temp + rand();\n\t}\n\treturn NULL;\n}\n\nvoid* Task3(){\n\twhile( end3 == 0 ) {\n\t\ttemp = temp - rand();\n\t}\n\treturn NULL;\n}\n\nvoid* Task4(){\n\twhile( end4 == 0 ) {\n\t\ttemp = temp * rand();\n\t}\n\treturn NULL;\n}\n\nvoid* Task5(){\n\twhile( end4 == 0 ) {\n\t\ttemp = temp / rand();\n\t}\n\treturn NULL;\n}\n</pre>\n\n==Future Work==\nNow that we have an operating system with sub 100us latency, our next step is to port the code for the camera over to the Linux side. Once that is done, we would like to be able to capture images at 1000fps or better and track the location of bright spots in the image.\n\n==Useful Links==\n[http://www.linuxjournal.com/article/232 Micro-Kernel Approach]\n\n[http://www.linuxjournal.com/magazine/real-time-linux-kernel-scheduler?page=0,0 Scheduling Approach]\n\n[http://rt.wiki.kernel.org/index.php/Main_Page Wiki on -rt Linux]\n\n[http://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO A \"Hello World\" Example]\n\n\n[[Category:Linux]]"
                    }
                ]
            }
        }
    }
}