mirror of
https://github.com/eddyem/eddys_snippets.git
synced 2025-12-06 10:45:12 +03:00
first commit
This commit is contained in:
commit
1a63497f04
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*~
|
||||||
|
*.bak
|
||||||
|
*.bck
|
||||||
|
*.o
|
||||||
|
.hg*
|
||||||
321
B-trees.c
Normal file
321
B-trees.c
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* B-trees.c - my realisation of binary serch trees
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "B-trees.h"
|
||||||
|
|
||||||
|
#ifndef Malloc
|
||||||
|
#define Malloc my_alloc
|
||||||
|
/**
|
||||||
|
* Memory allocation with control
|
||||||
|
* @param N - number of elements
|
||||||
|
* @param S - size of one element
|
||||||
|
* @return allocated memory or die
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef FREE
|
||||||
|
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EBUG
|
||||||
|
#ifndef DBG
|
||||||
|
#define DBG(...) do{fprintf(stderr, __VA_ARGS__);}while(0)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef DBG
|
||||||
|
#define DBG(...)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline uint32_t log_2(const uint32_t x) {
|
||||||
|
uint32_t y;
|
||||||
|
asm ( "\tbsr %1, %0\n"
|
||||||
|
: "=r"(y)
|
||||||
|
: "r" (x)
|
||||||
|
);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// later this function maybe mode difficult
|
||||||
|
void bt_destroy(BTree *node){
|
||||||
|
FREE(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get tree leaf nearest to v
|
||||||
|
BTree *bt_get(BTree *root, int v){
|
||||||
|
if(!root) return NULL;
|
||||||
|
int D = root->data;
|
||||||
|
if(D == v) return root;
|
||||||
|
if(root->data > v) return root->left;
|
||||||
|
else return root->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find leaf with v or nearest to it (prev)
|
||||||
|
// prev is nearest parent or NULL for root
|
||||||
|
BTree *bt_search(BTree *root, int v, BTree **prev){
|
||||||
|
if(!root){
|
||||||
|
if(prev) *prev = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
BTree *p;
|
||||||
|
do{
|
||||||
|
p = root;
|
||||||
|
root = bt_get(root, v);
|
||||||
|
}while(root && root->data != v);
|
||||||
|
if(prev){
|
||||||
|
if(p != root) *prev = p;
|
||||||
|
else *prev = NULL;
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
BTree *bt_create_leaf(int v){
|
||||||
|
BTree *node = Malloc(1, sizeof(BTree));
|
||||||
|
if(!node) return NULL;
|
||||||
|
node->data = v;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert new leaf (or leave it as is, if exists)
|
||||||
|
// return new node
|
||||||
|
BTree *bt_insert(BTree **root, int v){
|
||||||
|
BTree *closest = NULL, *node = NULL;
|
||||||
|
if(root && *root){
|
||||||
|
node = bt_search(*root, v, &closest);
|
||||||
|
if(node) return node; // value exists
|
||||||
|
}
|
||||||
|
node = bt_create_leaf(v);
|
||||||
|
if(!node) return NULL;
|
||||||
|
if(!closest){ // there's no values at all!
|
||||||
|
if(root) *root = node; // modify root node
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
// we have a closest node to our data, so create node and insert it:
|
||||||
|
int D = closest->data;
|
||||||
|
if(D > v) // there's no left leaf of closest node, add our node there
|
||||||
|
closest->left = node;
|
||||||
|
else
|
||||||
|
closest->right = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find mostly right element or NULL if no right children
|
||||||
|
BTree *max_leaf(BTree *root, BTree **prev){
|
||||||
|
if(!root)
|
||||||
|
return NULL;
|
||||||
|
BTree *ret = root->left;
|
||||||
|
if(prev) *prev = root;
|
||||||
|
while(ret->right){
|
||||||
|
if(prev) *prev = ret;
|
||||||
|
ret = ret->right;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// find mostly left element or NULL if no left children
|
||||||
|
BTree *min_leaf(BTree *root, BTree **prev){
|
||||||
|
if(!root) return NULL;
|
||||||
|
BTree *ret = root->right;
|
||||||
|
if(prev) *prev = root;
|
||||||
|
while(ret->left){
|
||||||
|
if(prev) *prev = ret;
|
||||||
|
ret = ret->left;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_tree(BTree *leaf){
|
||||||
|
if(!leaf){
|
||||||
|
DBG("null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DBG("(%d: ", leaf->data);
|
||||||
|
print_tree(leaf->left);
|
||||||
|
DBG(", ");
|
||||||
|
print_tree(leaf->right);
|
||||||
|
DBG(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove element, correct root if it is removed
|
||||||
|
void bt_remove(BTree **root, int v){
|
||||||
|
assert(root);
|
||||||
|
void conn(BTree *p, BTree *n, BTree *node){
|
||||||
|
DBG("connect: %d and %d through %d\n", p ? p->data : -1,
|
||||||
|
n? n->data : -1, node->data);
|
||||||
|
if(p->left == node) p->left = n;
|
||||||
|
else p->right = n;
|
||||||
|
}
|
||||||
|
void conn_node(BTree *prev, BTree *next, BTree *node){
|
||||||
|
if(!prev) *root = next; // this is a root
|
||||||
|
else conn(prev, next, node);
|
||||||
|
bt_destroy(node);
|
||||||
|
}
|
||||||
|
void node_subst(BTree *nold, BTree *nnew, BTree *pold, BTree *pnew){
|
||||||
|
DBG("Substitute: %d by %d\n", nold-> data, nnew->data);
|
||||||
|
if(!pold) *root = nnew; // this is a root
|
||||||
|
else conn(pold, nnew, nold); // connect parent of old element to new
|
||||||
|
conn(pnew, NULL, nnew); // remove node from it's parent
|
||||||
|
nnew->left = nold->left;
|
||||||
|
nnew->right = nold->right;
|
||||||
|
bt_destroy(nold);
|
||||||
|
}
|
||||||
|
BTree *node, *prev, *Lmax, *Rmin, *Lmp, *Rmp;
|
||||||
|
int L, R;
|
||||||
|
node = bt_search(*root, v, &prev);
|
||||||
|
if(!node) return; // there's no node v
|
||||||
|
if(!node->left){ // there's only right child or none at all
|
||||||
|
conn_node(prev, node->right, node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!node->right){ // the same but like mirror
|
||||||
|
conn_node(prev, node->left, node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we have situation that element have both children
|
||||||
|
// 1) find max from left child and min from right
|
||||||
|
Lmax = max_leaf(node, &Lmp);
|
||||||
|
L = Lmax->data;
|
||||||
|
Rmin = min_leaf(node, &Rmp);
|
||||||
|
R = Rmin->data;
|
||||||
|
// 2) now L is left max, R - right min
|
||||||
|
// we select the nearest to v
|
||||||
|
if(v - L > R - v){ // R is closest value, substitute node by Rmin
|
||||||
|
node_subst(node, Rmin, prev, Rmp);
|
||||||
|
}else{ // substitute node by Lmax
|
||||||
|
node_subst(node, Lmax, prev, Lmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return node need rotation
|
||||||
|
BTree *rotate_ccw(BTree *node, BTree *prev){
|
||||||
|
if(!node) return NULL;
|
||||||
|
#ifdef EBUG
|
||||||
|
DBG("\n\ntry to rotate near %d", node->data);
|
||||||
|
if(prev) DBG(", right element: %d", prev->data);
|
||||||
|
if(!node->right) DBG("\n");
|
||||||
|
#endif
|
||||||
|
if(!node->right) return node->left; // no right child -> return left
|
||||||
|
BTree *chld = node->right;
|
||||||
|
DBG(", child: %d\n", chld->data);
|
||||||
|
if(prev) prev->left = chld;
|
||||||
|
node->right = chld->left;
|
||||||
|
chld->left = node;
|
||||||
|
if(chld->right){DBG("R:%d\n",chld->right->data); return chld;} // need another rotation
|
||||||
|
else return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_to_vine(BTree **root){
|
||||||
|
assert(root); assert(*root);
|
||||||
|
BTree *node = *root, *prev;
|
||||||
|
while((*root)->right) *root = (*root)->right; // max element will be a new root
|
||||||
|
while(node && node->right)
|
||||||
|
node = rotate_ccw(node, NULL); // go to new root
|
||||||
|
prev = node; node = prev->left;
|
||||||
|
while(rotate_ccw(node, prev)){
|
||||||
|
node = prev->left;
|
||||||
|
if(!node->right){
|
||||||
|
prev = node;
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return node->left
|
||||||
|
BTree *rotate_cw(BTree *node, BTree *parent){ // rotate cw near node->left
|
||||||
|
if(!node) return NULL;
|
||||||
|
DBG("\n\ntry to rotate cw near %d\n", node->data);
|
||||||
|
#ifdef EBUG
|
||||||
|
if(parent)DBG("\t\tparent: %d\n", parent->data);
|
||||||
|
#endif
|
||||||
|
if(!node->left) return NULL;
|
||||||
|
BTree *chld = node->left, *rght = chld->right;
|
||||||
|
if(parent) parent->left = chld;
|
||||||
|
DBG("\t\t, child: %d\n", chld->data);
|
||||||
|
chld->right = node;
|
||||||
|
node->left = rght;
|
||||||
|
return chld;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make balanced tree
|
||||||
|
void vine_to_tree(BTree **root){
|
||||||
|
uint32_t depth, i;
|
||||||
|
BTree *node = *root, *prev;
|
||||||
|
for(depth = 0; node; depth++, node = node->left);
|
||||||
|
DBG("depth: %u; ", depth);
|
||||||
|
depth = log_2(depth);
|
||||||
|
DBG("log: %d\n", depth);
|
||||||
|
for(i = 0; i < depth; i++){
|
||||||
|
DBG("\nIteration %d\n", i);
|
||||||
|
node = *root;
|
||||||
|
prev = NULL;
|
||||||
|
if((*root)->left) *root = (*root)->left;
|
||||||
|
while(node && node->left){
|
||||||
|
node = rotate_cw(node, prev);
|
||||||
|
if(!node) break;
|
||||||
|
prev = node;
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
#define INS(X) do{if(!bt_insert(&root, X)) err(1, "alloc error");}while(0)
|
||||||
|
int main(void){
|
||||||
|
BTree *tp, *root = NULL;
|
||||||
|
int i, ins[] = {7,3,12,1,5,9,20,0,4,6,13,21};
|
||||||
|
void search_bt(){
|
||||||
|
printf("\nRoot: %d\nTree", root->data);
|
||||||
|
print_tree(root); printf("\n\n");
|
||||||
|
for(i = 0; i < 22; i++){
|
||||||
|
tp = bt_search(root, i, NULL);
|
||||||
|
printf("%d ", i);
|
||||||
|
if(!tp) printf("not ");
|
||||||
|
printf("found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
INS(ins[i]);
|
||||||
|
|
||||||
|
search_bt();
|
||||||
|
printf("now we delete leafs 4, 12 and 7 (old root!)\n");
|
||||||
|
bt_remove(&root, 4); bt_remove(&root, 12); bt_remove(&root, 7);
|
||||||
|
search_bt();
|
||||||
|
printf("add items 2, 4, 19\n");
|
||||||
|
INS(2); INS(4); INS(19);
|
||||||
|
search_bt();
|
||||||
|
printf("\nnow make a vine\n");
|
||||||
|
tree_to_vine(&root);
|
||||||
|
search_bt();
|
||||||
|
printf("\nAnd balance tree\n");
|
||||||
|
vine_to_tree(&root);
|
||||||
|
search_bt();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
41
B-trees.h
Normal file
41
B-trees.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* B-trees.h
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __B_TREES_H__
|
||||||
|
#define __B_TREES_H__
|
||||||
|
|
||||||
|
typedef struct btree_node{
|
||||||
|
int data;
|
||||||
|
struct btree_node *left, *right;
|
||||||
|
} BTree;
|
||||||
|
|
||||||
|
BTree *bt_get(BTree *root, int v);
|
||||||
|
BTree *bt_search(BTree *root, int v, BTree **prev);
|
||||||
|
BTree *bt_create_leaf(int v);
|
||||||
|
BTree *bt_insert(BTree **root, int v);
|
||||||
|
BTree *max_leaf(BTree *root, BTree **prev);
|
||||||
|
BTree *min_leaf(BTree *root, BTree **prev);
|
||||||
|
void bt_remove(BTree **root, int v);
|
||||||
|
|
||||||
|
void print_tree(BTree *leaf);
|
||||||
|
|
||||||
|
#endif // __B_TREES_H__
|
||||||
9
Makefile
Normal file
9
Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
CC = gcc
|
||||||
|
LDFLAGS =
|
||||||
|
DEFINES = -DSTANDALONE -DEBUG
|
||||||
|
CFLAGS = -Wall -Werror
|
||||||
|
|
||||||
|
all : fifo_lifo bidirectional_list B-trees simple_list
|
||||||
|
|
||||||
|
%: %.c
|
||||||
|
$(CC) $(LDFLAGS) $(CFLAGS) $(DEFINES) $< -o $@
|
||||||
13
README
Normal file
13
README
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This is a set of small usefull utilites & macros
|
||||||
|
|
||||||
|
|
||||||
|
bidirectional_list - simple list with operation of searching, inserting, deleting
|
||||||
|
B-trees - simple but slowly binary search trees with all main operations
|
||||||
|
fifo_lifo - simple stack-like queues
|
||||||
|
simple_list - 1-directional list with functions: add element; delete list
|
||||||
|
usefull_macros - a lot of different macros & functions
|
||||||
|
* safe memory allocation & freeing
|
||||||
|
* coloured output on tty & monochromeous on non-tty
|
||||||
|
* error/warning/debug macros
|
||||||
|
* MMAP files into memory
|
||||||
|
|
||||||
22
SMSD-1.5/Makefile
Normal file
22
SMSD-1.5/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
PROGRAM = client
|
||||||
|
LDFLAGS =
|
||||||
|
SRCS = client.c
|
||||||
|
CC = gcc
|
||||||
|
DEFINES = -D_XOPEN_SOURCE=501
|
||||||
|
CXX = gcc
|
||||||
|
CFLAGS = -Wall -Werror $(DEFINES)
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
all : $(PROGRAM) clean
|
||||||
|
$(PROGRAM) : $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
# some addition dependencies
|
||||||
|
# %.o: %.c
|
||||||
|
# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@
|
||||||
|
#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS)
|
||||||
|
# @touch $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(CXX.SRCS)
|
||||||
3
SMSD-1.5/README
Normal file
3
SMSD-1.5/README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Simple CLI interface for motorized linear translator Standa 8MT175-150 moving by SMSD-1.5 driver
|
||||||
|
|
||||||
|
Run ./client & press h for help.
|
||||||
460
SMSD-1.5/client.c
Normal file
460
SMSD-1.5/client.c
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
* client.c - simple terminal client for operationg with
|
||||||
|
* Standa's 8MT175-150 translator by SMSD-1.5 driver
|
||||||
|
*
|
||||||
|
* Hardware operates in microsterpping mode (1/16),
|
||||||
|
* max current = 1.2A
|
||||||
|
* voltage = 12V
|
||||||
|
* "0" of driver connected to end-switch at opposite from motor side
|
||||||
|
* switch of motor's side connected to "IN1"
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <termios.h> // tcsetattr
|
||||||
|
#include <unistd.h> // tcsetattr, close, read, write
|
||||||
|
#include <sys/ioctl.h> // ioctl
|
||||||
|
#include <stdio.h> // printf, getchar, fopen, perror
|
||||||
|
#include <stdlib.h> // exit
|
||||||
|
#include <sys/stat.h> // read
|
||||||
|
#include <fcntl.h> // read
|
||||||
|
#include <signal.h> // signal
|
||||||
|
#include <time.h> // time
|
||||||
|
#include <string.h> // memcpy, strcmp etc
|
||||||
|
#include <stdint.h> // int types
|
||||||
|
#include <sys/time.h> // gettimeofday
|
||||||
|
|
||||||
|
#define DBG(...) do{fprintf(stderr, __VA_ARGS__); }while(0)
|
||||||
|
|
||||||
|
//double t0; // start time
|
||||||
|
static int bus_error = 0; // last error of data output
|
||||||
|
enum{
|
||||||
|
NO_ERROR = 0, // normal execution
|
||||||
|
CODE_ERR, // error of exexuted program code
|
||||||
|
BUS_ERR, // data transmission error
|
||||||
|
COMMAND_ERR, // wrong command
|
||||||
|
CMD_DATA_ERR, // wrong data of command
|
||||||
|
UNDEFINED_ERR // something else wrong
|
||||||
|
};
|
||||||
|
|
||||||
|
FILE *fout = NULL; // file for messages duplicating
|
||||||
|
char *comdev = "/dev/ttyUSB0";
|
||||||
|
int BAUD_RATE = B9600;
|
||||||
|
struct termio oldtty, tty; // TTY flags
|
||||||
|
struct termios oldt, newt; // terminal flags
|
||||||
|
int comfd = -1; // TTY fd
|
||||||
|
|
||||||
|
int erase_ctrlr();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function for different purposes that need to know time intervals
|
||||||
|
* @return double value: time in seconds
|
||||||
|
*
|
||||||
|
double dtime(){
|
||||||
|
double t;
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
|
||||||
|
return t;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit & return terminal to old state
|
||||||
|
* @param ex_stat - status (return code)
|
||||||
|
*/
|
||||||
|
void quit(int ex_stat){
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
||||||
|
if(comfd > 0){
|
||||||
|
erase_ctrlr();
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
}
|
||||||
|
if(fout) fclose(fout);
|
||||||
|
printf("Exit! (%d)\n", ex_stat);
|
||||||
|
exit(ex_stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open & setup TTY, terminal
|
||||||
|
*/
|
||||||
|
void tty_init(){
|
||||||
|
// terminal without echo
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
//newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
newt.c_lflag &= ~(ICANON);
|
||||||
|
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0) quit(-2);
|
||||||
|
printf("\nOpen port...\n");
|
||||||
|
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
|
||||||
|
fprintf(stderr,"Can't use port %s\n",comdev);
|
||||||
|
quit(1);
|
||||||
|
}
|
||||||
|
printf(" OK\nGet current settings...\n");
|
||||||
|
if(ioctl(comfd,TCGETA,&oldtty) < 0) quit(-1); // Get settings
|
||||||
|
tty = oldtty;
|
||||||
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
|
tty.c_oflag = 0;
|
||||||
|
tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL | PARENB; // 9.6k, 8N1, RW, ignore line ctrl
|
||||||
|
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
|
tty.c_cc[VTIME] = 5;
|
||||||
|
if(ioctl(comfd,TCSETA,&tty) < 0) quit(-1); // set new mode
|
||||||
|
printf(" OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read characters from console without echo
|
||||||
|
* @return char readed
|
||||||
|
*/
|
||||||
|
int read_console(char *buf, size_t len){
|
||||||
|
int rb = 0;
|
||||||
|
ssize_t L = 0;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
fd_set rfds;
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(STDIN_FILENO, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 1000;
|
||||||
|
retval = select(1, &rfds, NULL, NULL, &tv);
|
||||||
|
if(retval){
|
||||||
|
if(FD_ISSET(STDIN_FILENO, &rfds)){
|
||||||
|
if(len < 2 || !buf) // command works as simple getchar
|
||||||
|
rb = getchar();
|
||||||
|
else{ // read all data from console buffer
|
||||||
|
if((L = read(STDIN_FILENO, buf, len)) > 0) rb = (int)L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getchar() without echo
|
||||||
|
* wait until at least one character pressed
|
||||||
|
* @return character readed
|
||||||
|
*/
|
||||||
|
int mygetchar(){ // аналог getchar() без необходимости жать Enter
|
||||||
|
int ret;
|
||||||
|
do ret = read_console(NULL, 1);
|
||||||
|
while(ret == 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from TTY
|
||||||
|
* @param buff (o) - buffer for data read
|
||||||
|
* @param length - buffer len
|
||||||
|
* @return amount of readed bytes
|
||||||
|
*/
|
||||||
|
size_t read_tty(uint8_t *buff, size_t length){
|
||||||
|
ssize_t L = 0;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(comfd, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 50000;
|
||||||
|
retval = select(comfd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if(retval < 1) return 0;
|
||||||
|
if(FD_ISSET(comfd, &rfds)){
|
||||||
|
if((L = read(comfd, buff, length)) < 1){
|
||||||
|
fprintf(stderr, "ERROR on bus, exit!\n");
|
||||||
|
exit(-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (size_t)L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait for answer from server
|
||||||
|
* @param sock - socket fd
|
||||||
|
* @return 0 in case of error or timeout, 1 in case of socket ready
|
||||||
|
*
|
||||||
|
int waittoread(int sock){
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval timeout;
|
||||||
|
int rc;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 1000;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(sock, &fds);
|
||||||
|
rc = select(sock+1, &fds, NULL, NULL, &timeout);
|
||||||
|
if(rc < 0){
|
||||||
|
perror("select failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(rc > 0 && FD_ISSET(sock, &fds)) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void help(){
|
||||||
|
printf("\n\nUse this commands:\n"
|
||||||
|
"0\tMove to end-switch 0\n"
|
||||||
|
"1\tMove to end-switch 1\n"
|
||||||
|
"Lxxx\tMake xxx steps toward zero's end-switch (0 main infinity)\n"
|
||||||
|
"Rxxx\tMake xxx steps toward end-switch 1 (0 main infinity)\n"
|
||||||
|
"S\tStop/start motor when program is running\n"
|
||||||
|
"A\tRun previous command again or stop when running\n"
|
||||||
|
"E\tErase previous program from controller's memory\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_tty(char *str, int L){
|
||||||
|
ssize_t D = write(comfd, str, L);
|
||||||
|
if(D != L){
|
||||||
|
fprintf(stderr, "ERROR on bus, exit!\n");
|
||||||
|
exit(-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define dup_pr(...) do{printf(__VA_ARGS__); if(fout) fprintf(fout, __VA_ARGS__);}while(0)
|
||||||
|
|
||||||
|
size_t read_ctrl_command(char *buf, size_t L){ // read data from controller to buffer buf
|
||||||
|
int i, j;
|
||||||
|
char *ptr = buf;
|
||||||
|
size_t R;
|
||||||
|
memset(buf, 0, L);
|
||||||
|
for(j = 0; j < L; j++, ptr++){
|
||||||
|
R = 0;
|
||||||
|
for(i = 0; i < 10 && !R; i++){
|
||||||
|
R = read_tty((uint8_t*)ptr, 1);
|
||||||
|
}
|
||||||
|
if(!R){j--; break;} // nothing to read
|
||||||
|
if(*ptr == '*') // read only one command
|
||||||
|
break;
|
||||||
|
if(*ptr < ' '){ // omit spaces & non-characters
|
||||||
|
j--; ptr--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (size_t) j + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_command(char *cmd){
|
||||||
|
int L = strlen(cmd);
|
||||||
|
size_t R = 0;
|
||||||
|
char ans[256];
|
||||||
|
write_tty(cmd, L);
|
||||||
|
R = read_ctrl_command(ans, 255);
|
||||||
|
DBG("readed: %s (cmd: %s, R = %zd, L = %d)\n", ans, cmd, R, L);
|
||||||
|
if(!R || (strncmp(ans, cmd, L) != 0)){
|
||||||
|
fprintf(stderr, "Error: controller doesn't respond (answer: %s)\n", ans);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
R = read_ctrl_command(ans, 255);
|
||||||
|
DBG("readed: %s\n", ans);
|
||||||
|
if(!R){ // controller is running or error
|
||||||
|
fprintf(stderr, "Controller doesn't answer!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
bus_error = NO_ERROR;
|
||||||
|
//if(strncasecmp(ptr, "HM")
|
||||||
|
//else
|
||||||
|
if( strncmp(ans, "E10*", 4) != 0 &&
|
||||||
|
strncmp(ans, "E14*", 4) != 0 &&
|
||||||
|
strncmp(ans, "E12*", 4) != 0){
|
||||||
|
fprintf(stderr, "Error in controller: ");
|
||||||
|
if(strncmp(ans, "E13*", 4) == 0){
|
||||||
|
bus_error = CODE_ERR;
|
||||||
|
fprintf(stderr, "runtime");
|
||||||
|
}else if(strncmp(ans, "E15*", 4) == 0){
|
||||||
|
bus_error = BUS_ERR;
|
||||||
|
fprintf(stderr, "databus");
|
||||||
|
}else if(strncmp(ans, "E16*", 4) == 0){
|
||||||
|
bus_error = COMMAND_ERR;
|
||||||
|
fprintf(stderr, "command");
|
||||||
|
}else if(strncmp(ans, "E19*", 4) == 0){
|
||||||
|
bus_error = CMD_DATA_ERR;
|
||||||
|
fprintf(stderr, "command data");
|
||||||
|
}else{
|
||||||
|
bus_error = UNDEFINED_ERR;
|
||||||
|
fprintf(stderr, "undefined (%s)", ans);
|
||||||
|
}
|
||||||
|
fprintf(stderr, " error\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DBG("ALL OK\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
int send5times(char *cmd){ // sends command 'cmd' up to 5 times (if errors), return 0 in case of false
|
||||||
|
int N, R = 0;
|
||||||
|
for(N = 0; N < 5 && !R; N++){
|
||||||
|
R = send_command(cmd);
|
||||||
|
}
|
||||||
|
return R;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
int erase_ctrlr(){
|
||||||
|
char *errmsg = "\n\nCan't erase controller's memory: some errors occured!\n\n";
|
||||||
|
#define safely_send(x) do{ if(bus_error != NO_ERROR){ \
|
||||||
|
fprintf(stderr, errmsg); return 0;} send_command(x); }while(0)
|
||||||
|
if(!send_command("LD1*")){ // start writing a program
|
||||||
|
if(bus_error == COMMAND_ERR){ // motor is moving
|
||||||
|
printf("Found running program, stop it\n");
|
||||||
|
if(send_command("ST1*"))
|
||||||
|
send_command("LD1*");
|
||||||
|
}else{
|
||||||
|
fprintf(stderr, "Controller doesn't answer: try to press S or E\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
safely_send("BG*"); // move address pointer to beginning
|
||||||
|
safely_send("DS*"); // turn off motor
|
||||||
|
safely_send("ED*"); // end of program
|
||||||
|
if(bus_error != NO_ERROR){
|
||||||
|
fprintf(stderr, errmsg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void con_sig(int rb){
|
||||||
|
int stepsN = 0, got_command = 0;
|
||||||
|
char command[256];
|
||||||
|
if(rb < 1) return;
|
||||||
|
if(rb == 'q') quit(0); // q == exit
|
||||||
|
if(rb == 'L' || rb == 'R'){
|
||||||
|
if(!fgets(command, 255, stdin)){
|
||||||
|
fprintf(stderr, "You should give amount of steps after commands 'L' and 'R'\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stepsN = atoi(command) * 16; // microstepping
|
||||||
|
if(stepsN < 0 || stepsN > 10000000){
|
||||||
|
fprintf(stderr, "\n\nSteps amount should be > -1 and < 625000 (0 means infinity)!\n\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define Die_on_error(arg) do{if(!send_command(arg)) goto erase_;}while(0)
|
||||||
|
if(strchr("LR01", rb)){ // command to execute
|
||||||
|
got_command = 1;
|
||||||
|
if(!send_command("LD1*")){ // start writing a program
|
||||||
|
fprintf(stderr, "Error: previous program is running!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Die_on_error("BG*"); // move address pointer to beginning
|
||||||
|
Die_on_error("EN*"); // enable power
|
||||||
|
Die_on_error("SD10000*"); // set speed to max (625 steps per second with 1/16)
|
||||||
|
}
|
||||||
|
switch(rb){
|
||||||
|
case 'h':
|
||||||
|
help();
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
Die_on_error("DL*");
|
||||||
|
Die_on_error("HM*");
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
Die_on_error("DR*");
|
||||||
|
Die_on_error("ML*");
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
Die_on_error("DR*");
|
||||||
|
if(stepsN)
|
||||||
|
sprintf(command, "MV%d*", stepsN);
|
||||||
|
else
|
||||||
|
sprintf(command, "MV*");
|
||||||
|
Die_on_error(command);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
Die_on_error("DL*");
|
||||||
|
if(stepsN)
|
||||||
|
sprintf(command, "MV%d*", stepsN);
|
||||||
|
else
|
||||||
|
sprintf(command, "MV*");
|
||||||
|
Die_on_error(command);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
Die_on_error("PS1*");
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
Die_on_error("ST1*");
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
erase_ctrlr();
|
||||||
|
break;
|
||||||
|
/* default:
|
||||||
|
cmd = (uint8_t) rb;
|
||||||
|
write(comfd, &cmd, 1);*/
|
||||||
|
}
|
||||||
|
if(got_command){ // there was some command: write ending words
|
||||||
|
Die_on_error("DS*"); // turn off power from motor at end
|
||||||
|
Die_on_error("ED*"); // signal about command end
|
||||||
|
Die_on_error("ST1*");// start program
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
erase_:
|
||||||
|
if(!erase_ctrlr()) quit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get integer value from buffer
|
||||||
|
* @param buff (i) - buffer with int
|
||||||
|
* @param len - length of data in buffer (could be 2 or 4)
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
uint32_t get_int(uint8_t *buff, size_t len){
|
||||||
|
int i;
|
||||||
|
printf("read %zd bytes: ", len);
|
||||||
|
for(i = 0; i < len; i++) printf("0x%x ", buff[i]);
|
||||||
|
printf("\n");
|
||||||
|
if(len != 2 && len != 4){
|
||||||
|
fprintf(stdout, "Bad data length!\n");
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
uint32_t data = 0;
|
||||||
|
uint8_t *i8 = (uint8_t*) &data;
|
||||||
|
if(len == 2) memcpy(i8, buff, 2);
|
||||||
|
else memcpy(i8, buff, 4);
|
||||||
|
return data;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]){
|
||||||
|
int rb;
|
||||||
|
uint8_t buff[128];
|
||||||
|
size_t L;
|
||||||
|
if(argc == 2){
|
||||||
|
fout = fopen(argv[1], "a");
|
||||||
|
if(!fout){
|
||||||
|
perror("Can't open output file");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
setbuf(fout, NULL);
|
||||||
|
}
|
||||||
|
tty_init();
|
||||||
|
signal(SIGTERM, quit); // kill (-15)
|
||||||
|
signal(SIGINT, quit); // ctrl+C
|
||||||
|
signal(SIGQUIT, SIG_IGN); // ctrl+\ .
|
||||||
|
signal(SIGTSTP, SIG_IGN); // ctrl+Z
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
if(!erase_ctrlr()) quit(1);
|
||||||
|
//t0 = dtime();
|
||||||
|
while(1){
|
||||||
|
rb = read_console(NULL, 1);
|
||||||
|
if(rb > 0) con_sig(rb);
|
||||||
|
L = read_tty(buff, 127);
|
||||||
|
if(L){
|
||||||
|
buff[L] = 0;
|
||||||
|
printf("%s", buff);
|
||||||
|
if(fout) fprintf(fout, "%zd\t%s\n", time(NULL), buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Trinamic/client/Makefile
Normal file
22
Trinamic/client/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
PROGRAM = client
|
||||||
|
LDFLAGS =
|
||||||
|
SRCS = client.c
|
||||||
|
CC = gcc
|
||||||
|
DEFINES = -D_XOPEN_SOURCE=501
|
||||||
|
CXX = gcc
|
||||||
|
CFLAGS = -Wall -Werror $(DEFINES)
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
all : $(PROGRAM) clean
|
||||||
|
$(PROGRAM) : $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
# some addition dependencies
|
||||||
|
# %.o: %.c
|
||||||
|
# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@
|
||||||
|
#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS)
|
||||||
|
# @touch $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(CXX.SRCS)
|
||||||
495
Trinamic/client/client.c
Normal file
495
Trinamic/client/client.c
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
/*
|
||||||
|
* client.c - simple terminal client
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include <termios.h> // tcsetattr
|
||||||
|
#include <unistd.h> // tcsetattr, close, read, write
|
||||||
|
#include <sys/ioctl.h> // ioctl
|
||||||
|
#include <stdio.h> // printf, getchar, fopen, perror
|
||||||
|
#include <stdlib.h> // exit
|
||||||
|
#include <sys/stat.h> // read
|
||||||
|
#include <fcntl.h> // read
|
||||||
|
#include <signal.h> // signal
|
||||||
|
#include <time.h> // time
|
||||||
|
#include <string.h> // memcpy
|
||||||
|
#include <stdint.h> // int types
|
||||||
|
#include <sys/time.h> // gettimeofday
|
||||||
|
|
||||||
|
#define BUFLEN 1024
|
||||||
|
|
||||||
|
double t0; // start time
|
||||||
|
|
||||||
|
uint32_t motor_speed = 10; // motor speed
|
||||||
|
|
||||||
|
FILE *fout = NULL; // file for messages duplicating
|
||||||
|
char *comdev = "/dev/ttyUSB0";
|
||||||
|
int BAUD_RATE = B115200;
|
||||||
|
struct termio oldtty, tty; // TTY flags
|
||||||
|
struct termios oldt, newt; // terminal flags
|
||||||
|
int comfd; // TTY fd
|
||||||
|
|
||||||
|
#define DBG(...) do{fprintf(stderr, __VA_ARGS__);}while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function for different purposes that need to know time intervals
|
||||||
|
* @return double value: time in seconds
|
||||||
|
*
|
||||||
|
double dtime(){
|
||||||
|
double t;
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
|
||||||
|
return t;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit & return terminal to old state
|
||||||
|
* @param ex_stat - status (return code)
|
||||||
|
*/
|
||||||
|
void quit(int ex_stat){
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
if(fout) fclose(fout);
|
||||||
|
printf("Exit! (%d)\n", ex_stat);
|
||||||
|
exit(ex_stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_command(uint8_t *ninebytes){
|
||||||
|
uint8_t crc = 0;
|
||||||
|
int i;
|
||||||
|
printf("send: ");
|
||||||
|
for(i = 0; i < 8; crc += ninebytes[i++])
|
||||||
|
printf("%u,", ninebytes[i]);
|
||||||
|
ninebytes[8] = crc;
|
||||||
|
printf("%u\n",ninebytes[8]);
|
||||||
|
if(9 != write(comfd, ninebytes, 9)){
|
||||||
|
perror("Can't write to Trinamic");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open & setup TTY, terminal
|
||||||
|
*/
|
||||||
|
void tty_init(){
|
||||||
|
// terminal without echo
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0) quit(-2);
|
||||||
|
printf("\nOpen port...\n");
|
||||||
|
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
|
||||||
|
fprintf(stderr,"Can't use port %s\n",comdev);
|
||||||
|
quit(1);
|
||||||
|
}
|
||||||
|
printf(" OK\nGet current settings...\n");
|
||||||
|
if(ioctl(comfd,TCGETA,&oldtty) < 0) quit(-1); // Get settings
|
||||||
|
tty = oldtty;
|
||||||
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
|
tty.c_oflag = 0;
|
||||||
|
tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
|
||||||
|
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
|
tty.c_cc[VTIME] = 5;
|
||||||
|
if(ioctl(comfd,TCSETA,&tty) < 0) quit(-1); // set new mode
|
||||||
|
printf(" OK\n");
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getchar() without echo
|
||||||
|
* wait until at least one character pressed
|
||||||
|
* @return character readed
|
||||||
|
*
|
||||||
|
int mygetchar(){
|
||||||
|
int ret;
|
||||||
|
do ret = read_console();
|
||||||
|
while(ret == 0);
|
||||||
|
return ret;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read both tty & console
|
||||||
|
* @param buff (o) - buffer for messages readed from tty or NULL (to omit readed info)
|
||||||
|
* @param length (io) - buff's length (return readed len or 0)
|
||||||
|
* @param rb (io) - byte readed from console, -1 if nothing read, NULL to omit console reading
|
||||||
|
* @return 1 if something was readed here or there
|
||||||
|
*/
|
||||||
|
int read_tty_and_console(uint8_t *buff, size_t *length, int *rb){
|
||||||
|
ssize_t L;
|
||||||
|
ssize_t l;
|
||||||
|
uint8_t buff1[BUFLEN+1];
|
||||||
|
size_t buffsz;
|
||||||
|
struct timeval tv;
|
||||||
|
int sel, retval = 0;
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(STDIN_FILENO, &rfds);
|
||||||
|
FD_SET(comfd, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 10000;
|
||||||
|
sel = select(comfd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if(!buff){
|
||||||
|
buffsz = BUFLEN;
|
||||||
|
buff = buff1;
|
||||||
|
}else{
|
||||||
|
buffsz = *length;
|
||||||
|
}
|
||||||
|
if(sel > 0){
|
||||||
|
if(rb){
|
||||||
|
if(FD_ISSET(STDIN_FILENO, &rfds)){
|
||||||
|
*rb = getchar();
|
||||||
|
retval = 1;
|
||||||
|
}else{
|
||||||
|
*rb = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(FD_ISSET(comfd, &rfds)){
|
||||||
|
if((L = read(comfd, buff, buffsz)) < 1){ // disconnect or other troubles
|
||||||
|
fprintf(stderr, "USB error or disconnected!\n");
|
||||||
|
quit(1);
|
||||||
|
}else{
|
||||||
|
if(L == 0){ // USB disconnected
|
||||||
|
fprintf(stderr, "USB disconnected!\n");
|
||||||
|
quit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// all OK continue reading
|
||||||
|
//DBG("readed %zd bytes, try more.. ", L);
|
||||||
|
buffsz -= L;
|
||||||
|
usleep(10000);
|
||||||
|
while(buffsz > 0 && (l = read(comfd, buff+L, buffsz)) > 0){
|
||||||
|
L += l;
|
||||||
|
buffsz -= l;
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
//DBG("full len: %zd\n", L);
|
||||||
|
|
||||||
|
if(length) *length = (size_t) L;
|
||||||
|
retval = 1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(length) *length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t get_integer(uint8_t *buff){
|
||||||
|
int32_t val;
|
||||||
|
int ii;
|
||||||
|
uint8_t *valptr = (uint8_t*) &val + 3;
|
||||||
|
for(ii = 4; ii < 8; ii++)
|
||||||
|
*valptr-- = buff[ii];
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void help(){
|
||||||
|
printf("Use this commands:\n"
|
||||||
|
"h\tShow this help\n"
|
||||||
|
"+/-\tIncrease/decrease speed by 10%% or 1\n"
|
||||||
|
"M/m\tMove to relative position\n"
|
||||||
|
"R/L\tRotate motor 1 right/left\n"
|
||||||
|
"r/l\tRotate motor 2 right/left\n"
|
||||||
|
"S/s\tStop 1st or 2nd motor\n"
|
||||||
|
"W/w\tSet ustepping to 1/16\n"
|
||||||
|
"E/e\tSet ustepping to 1/4\n"
|
||||||
|
"Z/z\tSet to zero current position\n"
|
||||||
|
"G/g\tGet current position\n"
|
||||||
|
"A/a\tGet ALL information about\n"
|
||||||
|
"q\tQuit\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
char *name; // command name
|
||||||
|
uint8_t cmd; // command number
|
||||||
|
} custom_command;
|
||||||
|
|
||||||
|
void get_information(uint8_t *com){
|
||||||
|
int i;
|
||||||
|
uint8_t buff[9];
|
||||||
|
size_t L;
|
||||||
|
custom_command info[] = {
|
||||||
|
{"target position", 0},
|
||||||
|
{"actual position", 1},
|
||||||
|
{"target speed", 2},
|
||||||
|
{"actual speed", 3},
|
||||||
|
{"max positioning speed", 4},
|
||||||
|
{"max acceleration", 5},
|
||||||
|
{"abs. max current", 6},
|
||||||
|
{"standby current", 7},
|
||||||
|
{"target pos. reached", 8},
|
||||||
|
{"ref. switch status", 9},
|
||||||
|
{"right limit switch status", 10},
|
||||||
|
{"left limit switch status", 11},
|
||||||
|
{"right limit switch disable", 12},
|
||||||
|
{"left limit switch disable", 13},
|
||||||
|
{"minimum speed", 130},
|
||||||
|
{"actual acceleration", 135},
|
||||||
|
{"ramp mode", 138},
|
||||||
|
{"microstep resolution", 140},
|
||||||
|
{"soft stop flag", 149},
|
||||||
|
{"ramp divisor", 153},
|
||||||
|
{"pulse divisor", 154},
|
||||||
|
{"referencing mode", 193},
|
||||||
|
{"referencing search speed", 194},
|
||||||
|
{"referencing switch speed", 195},
|
||||||
|
{"distance end switches", 196},
|
||||||
|
{"mixed decay threshold", 203},
|
||||||
|
{"freewheeling", 204},
|
||||||
|
{"stall detection threshold", 205},
|
||||||
|
{"actual load value", 206},
|
||||||
|
{"driver error flags", 208},
|
||||||
|
{"fullstep threshold", 211},
|
||||||
|
{"power down delay", 214},
|
||||||
|
{NULL, 0}
|
||||||
|
};
|
||||||
|
printf("\n");
|
||||||
|
for(i = 0; info[i].name; i++){
|
||||||
|
printf("%s (%u) ", info[i].name, info[i].cmd);
|
||||||
|
com[2] = info[i].cmd;
|
||||||
|
if(send_command(com)){ // command send OK
|
||||||
|
L = 9;
|
||||||
|
if(read_tty_and_console(buff, &L, NULL)){ // get answer
|
||||||
|
if(L == 9 && buff[2] > 6){ // answer's length is right && no error in status
|
||||||
|
int32_t val = get_integer(buff);
|
||||||
|
printf("= %d\n", val);
|
||||||
|
//for(ii = 0; ii < 9; ii++) printf("%u,",buff[ii]);
|
||||||
|
}else printf("L=%zd (stat: %u)\n", L, buff[2]);
|
||||||
|
}}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void con_sig(int rb){
|
||||||
|
if(rb < 1) return;
|
||||||
|
uint32_t value = motor_speed; // here is the value to send
|
||||||
|
/*
|
||||||
|
* trinamic binary format (bytes):
|
||||||
|
* 0 - address
|
||||||
|
* 1 - command
|
||||||
|
* 2 - type
|
||||||
|
* 3 - motor/bank number
|
||||||
|
* 4 \
|
||||||
|
* 5 | value (msb first!)
|
||||||
|
* 6 |
|
||||||
|
* 7 /
|
||||||
|
* 8 - checksum (sum of bytes 0..8)
|
||||||
|
*/
|
||||||
|
uint8_t command[9] = {1,0,0,0,0,0,0,0,0}; // command to sent
|
||||||
|
int i, V;
|
||||||
|
uint8_t *cmd = &command[1]; // byte 1 of message is command itself
|
||||||
|
if(rb < 1) return;
|
||||||
|
if(rb == 'q' || rb == 'Q') quit(0);
|
||||||
|
if(rb == 'h' || rb == 'H'){
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void change_motor_speed(int dir){
|
||||||
|
uint32_t Delta = motor_speed / 10, newspd;
|
||||||
|
int i;
|
||||||
|
if(Delta == 0) Delta = 1;
|
||||||
|
if(dir > 0){
|
||||||
|
newspd = motor_speed + Delta;
|
||||||
|
if(newspd > motor_speed) motor_speed = newspd;
|
||||||
|
else motor_speed = (uint32_t)0xffffffffUL;
|
||||||
|
}else{
|
||||||
|
newspd = motor_speed - Delta;
|
||||||
|
if(newspd < motor_speed && newspd) motor_speed = newspd;
|
||||||
|
else motor_speed = 1;
|
||||||
|
}
|
||||||
|
printf("\nspeed changed to %u\n", motor_speed);
|
||||||
|
*cmd = 5; // SAP
|
||||||
|
command[2] = 4; // max pos speed
|
||||||
|
uint8_t *spd = (uint8_t*) &motor_speed + 3;
|
||||||
|
for(i = 4; i < 8; i++)
|
||||||
|
command[i] = *spd--;
|
||||||
|
send_command(command); // store new speed
|
||||||
|
read_tty_and_console(NULL, NULL, NULL);
|
||||||
|
command[3] = 2; // and for second motor
|
||||||
|
send_command(command);
|
||||||
|
read_tty_and_console(NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
if(rb == '+'){
|
||||||
|
change_motor_speed(1);
|
||||||
|
}else if(rb == '-'){
|
||||||
|
change_motor_speed(-1);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Now we must analize commands: all letters exept Q/q & H/h are for motors commands
|
||||||
|
*/
|
||||||
|
if(rb > 'a'-1 && rb < 'z'+1){ // small letter -> change motor number to 2
|
||||||
|
command[3] = 2;
|
||||||
|
}else if(rb > 'A'-1 && rb < 'Z'+1){ // capital letter -> change to small
|
||||||
|
rb += 'a' - 'A';
|
||||||
|
}else return; // wrong command
|
||||||
|
switch(rb){
|
||||||
|
case 'r': // move motor right
|
||||||
|
*cmd = 1; // ROR
|
||||||
|
break;
|
||||||
|
case 'l': // move motor left
|
||||||
|
*cmd = 2; // ROL
|
||||||
|
break;
|
||||||
|
case 's': // stop motor
|
||||||
|
*cmd = 3; // STP
|
||||||
|
break;
|
||||||
|
case 'w': // 1/16
|
||||||
|
*cmd = 5; // SAP
|
||||||
|
command[2] = 140; // ustep resolution
|
||||||
|
value = 4;
|
||||||
|
break;
|
||||||
|
case 'e': // 1/4
|
||||||
|
*cmd = 5; // SAP
|
||||||
|
command[2] = 140; // ustep resolution
|
||||||
|
value = 2;
|
||||||
|
break;
|
||||||
|
case 'g': // get current position
|
||||||
|
*cmd = 6; // GAP
|
||||||
|
command[2] = 1; // actual position
|
||||||
|
break;
|
||||||
|
case 'a': // get everything!
|
||||||
|
*cmd = 6; // GAP
|
||||||
|
get_information(command);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case 'z': // set to zero current position
|
||||||
|
*cmd = 5; // SAP
|
||||||
|
command[2] = 1; // actual position
|
||||||
|
value = 0;
|
||||||
|
break;
|
||||||
|
case 'm': // move to relative position
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to original state
|
||||||
|
if(scanf("%d", &V) > 0 && V){
|
||||||
|
*cmd = 4; // MVP
|
||||||
|
command[2] = 1; // REL
|
||||||
|
value = (uint32_t) V;
|
||||||
|
//if(V > 0) value = (uint32_t) V;
|
||||||
|
//else value =
|
||||||
|
}
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt); // omit echo
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(*cmd){
|
||||||
|
// copy param to buffer
|
||||||
|
uint8_t *spd = (uint8_t*) &value + 3;
|
||||||
|
for(i = 4; i < 8; i++)
|
||||||
|
command[i] = *spd--;
|
||||||
|
send_command(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get integer value from buffer
|
||||||
|
* @param buff (i) - buffer with int
|
||||||
|
* @param len - length of data in buffer (could be 2 or 4)
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
uint32_t get_int(uint8_t *buff, size_t len){
|
||||||
|
if(len != 2 && len != 4){
|
||||||
|
fprintf(stdout, "Bad data length!\n");
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
uint32_t data = 0;
|
||||||
|
uint8_t *i8 = (uint8_t*) &data;
|
||||||
|
if(len == 2) memcpy(i8, buff, 2);
|
||||||
|
else memcpy(i8, buff, 4);
|
||||||
|
return data;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy line by line buffer buff to file removing cmd starting from newline
|
||||||
|
* @param buffer - data to put into file
|
||||||
|
* @param cmd - symbol to remove from line startint (if found, change *cmd to (-1)
|
||||||
|
* or NULL, (-1) if no command to remove
|
||||||
|
*/
|
||||||
|
void copy_buf_to_file(uint8_t *buffer, int *cmd){
|
||||||
|
uint8_t *buff, *line, *ptr;
|
||||||
|
if(!cmd || *cmd < 0){
|
||||||
|
fprintf(fout, "%s", buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buff = (uint8_t*)strdup((char*)buffer), ptr = buff;
|
||||||
|
do{
|
||||||
|
if(!*ptr) break;
|
||||||
|
if(ptr[0] == (char)*cmd){
|
||||||
|
*cmd = -1;
|
||||||
|
ptr++;
|
||||||
|
if(ptr[0] == '\n') ptr++;
|
||||||
|
if(!*ptr) break;
|
||||||
|
}
|
||||||
|
line = ptr;
|
||||||
|
ptr = (uint8_t*)strchr((char*)buff, '\n');
|
||||||
|
if(ptr){
|
||||||
|
*ptr++ = 0;
|
||||||
|
//fprintf(fout, "%s\n", line);
|
||||||
|
}//else
|
||||||
|
//fprintf(fout, "%s", line); // no newline found in buffer
|
||||||
|
fprintf(fout, "%s\n", line);
|
||||||
|
}while(ptr);
|
||||||
|
free(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]){
|
||||||
|
int rb, oldcmd = -1;
|
||||||
|
uint8_t buff[BUFLEN+1];
|
||||||
|
size_t L;
|
||||||
|
if(argc == 2){
|
||||||
|
fout = fopen(argv[1], "a");
|
||||||
|
if(!fout){
|
||||||
|
perror("Can't open output file");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
setbuf(fout, NULL);
|
||||||
|
}
|
||||||
|
tty_init();
|
||||||
|
signal(SIGTERM, quit); // kill (-15)
|
||||||
|
signal(SIGINT, quit); // ctrl+C
|
||||||
|
signal(SIGQUIT, SIG_IGN); // ctrl+\ .
|
||||||
|
signal(SIGTSTP, SIG_IGN); // ctrl+Z
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
//t0 = dtime();
|
||||||
|
while(1){
|
||||||
|
L = BUFLEN;
|
||||||
|
if(read_tty_and_console(buff, &L, &rb)){
|
||||||
|
if(rb > 0){
|
||||||
|
con_sig(rb);
|
||||||
|
oldcmd = rb;
|
||||||
|
}
|
||||||
|
if(L > 0){
|
||||||
|
uint8_t *ptr = buff;
|
||||||
|
int ii;
|
||||||
|
int32_t val = get_integer(buff);
|
||||||
|
printf("value = %d (full answer: ", val);
|
||||||
|
for(ii = L - 1; ii > 0; ii--){
|
||||||
|
uint8_t C = *ptr++;
|
||||||
|
printf("%u", C);
|
||||||
|
//if(C > 31) printf("(%c)", C);
|
||||||
|
printf(",");
|
||||||
|
}
|
||||||
|
printf("%u)\n", *ptr);
|
||||||
|
if(fout){
|
||||||
|
copy_buf_to_file(buff, &oldcmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Trinamic/websock_simple/Makefile
Normal file
22
Trinamic/websock_simple/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
PROGRAM = websocktest
|
||||||
|
LDFLAGS = $(shell pkg-config --libs libwebsockets)
|
||||||
|
SRCS = test.c
|
||||||
|
CC = gcc
|
||||||
|
DEFINES = -D_XOPEN_SOURCE=501 -DCUR_PATH=\"$(shell pwd)\"
|
||||||
|
CXX = gcc
|
||||||
|
CFLAGS = -Wall -Werror -Wextra $(DEFINES) $(shell pkg-config --cflags libwebsockets)
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
all : $(PROGRAM) clean
|
||||||
|
$(PROGRAM) : $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
# some addition dependencies
|
||||||
|
# %.o: %.c
|
||||||
|
# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@
|
||||||
|
#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS)
|
||||||
|
# @touch $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(CXX.SRCS)
|
||||||
183
Trinamic/websock_simple/test.c
Normal file
183
Trinamic/websock_simple/test.c
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
|
||||||
|
#include <termios.h> // tcsetattr
|
||||||
|
#include <stdint.h> // int types
|
||||||
|
#include <sys/stat.h> // read
|
||||||
|
#include <fcntl.h> // read
|
||||||
|
|
||||||
|
|
||||||
|
#define _U_ __attribute__((__unused__))
|
||||||
|
|
||||||
|
int force_exit = 0;
|
||||||
|
|
||||||
|
uint8_t buf[9];
|
||||||
|
|
||||||
|
char *comdev = "/dev/ttyUSB0";
|
||||||
|
int BAUD_RATE = B115200;
|
||||||
|
int comfd = -1; // TTY fd
|
||||||
|
int send_command(uint8_t *ninebytes){
|
||||||
|
uint8_t crc = 0;
|
||||||
|
int i;
|
||||||
|
printf("send: ");
|
||||||
|
for(i = 0; i < 8; crc += ninebytes[i++])
|
||||||
|
printf("%u,", ninebytes[i]);
|
||||||
|
ninebytes[8] = crc;
|
||||||
|
printf("%u\n",ninebytes[8]);
|
||||||
|
if(9 != write(comfd, ninebytes, 9)){
|
||||||
|
perror("Can't write to Trinamic");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillbuf(char *command){
|
||||||
|
memset(buf, 0, 9);
|
||||||
|
buf[0] = 1; // controller #
|
||||||
|
if(command[1] == 'X') buf[3] = 2; // X motor -> #2
|
||||||
|
else buf[3] = 0; // Y motor -> #0
|
||||||
|
if(command[0] == 'D'){ // start moving
|
||||||
|
if(command[2] == '+') buf[1] = 1; // ROR
|
||||||
|
else buf[1] = 2; // ROL
|
||||||
|
}else{ // stop
|
||||||
|
buf[1] = 3; // STP
|
||||||
|
}
|
||||||
|
buf[7] = 50; // speed = 50
|
||||||
|
send_command(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_motor(char *where){
|
||||||
|
printf("moving %s\n", where);
|
||||||
|
|
||||||
|
}
|
||||||
|
void stop_motor(char *where){
|
||||||
|
printf("stoping %s\n", where);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
my_protocol_callback(_U_ struct libwebsocket_context *context,
|
||||||
|
_U_ struct libwebsocket *wsi,
|
||||||
|
enum libwebsocket_callback_reasons reason,
|
||||||
|
_U_ void *user, void *in, _U_ size_t len)
|
||||||
|
{
|
||||||
|
//int n, m;
|
||||||
|
//unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
|
||||||
|
// LWS_SEND_BUFFER_POST_PADDING];
|
||||||
|
//unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
|
||||||
|
|
||||||
|
char *msg = (char*) in;
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
|
||||||
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
|
printf("New Connection\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LWS_CALLBACK_RECEIVE:
|
||||||
|
fillbuf(msg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//**************************************************************************//
|
||||||
|
/* list of supported protocols and callbacks */
|
||||||
|
//**************************************************************************//
|
||||||
|
static struct libwebsocket_protocols protocols[] = {
|
||||||
|
/* first protocol must always be HTTP handler */
|
||||||
|
|
||||||
|
{
|
||||||
|
"XY-protocol", // name
|
||||||
|
my_protocol_callback, // callback
|
||||||
|
30, // per_session_data_size
|
||||||
|
10, // max frame size / rx buffer
|
||||||
|
0, NULL, 0
|
||||||
|
},
|
||||||
|
{ NULL, NULL, 0, 0, 0, NULL, 0} /* terminator */
|
||||||
|
};
|
||||||
|
//**************************************************************************//
|
||||||
|
void sighandler(_U_ int sig){
|
||||||
|
close(comfd);
|
||||||
|
force_exit = 1;
|
||||||
|
}
|
||||||
|
//**************************************************************************//
|
||||||
|
//**************************************************************************//
|
||||||
|
int main(_U_ int argc, _U_ char **argv)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
struct libwebsocket_context *context;
|
||||||
|
int opts = 0;
|
||||||
|
const char *iface = NULL;
|
||||||
|
int syslog_options = LOG_PID | LOG_PERROR;
|
||||||
|
//unsigned int oldus = 0;
|
||||||
|
struct lws_context_creation_info info;
|
||||||
|
|
||||||
|
int debug_level = 7;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof info);
|
||||||
|
info.port = 9999;
|
||||||
|
|
||||||
|
signal(SIGINT, sighandler);
|
||||||
|
|
||||||
|
printf("\nOpen port...\n");
|
||||||
|
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
|
||||||
|
fprintf(stderr,"Can't use port %s\n",comdev);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* we will only try to log things according to our debug_level */
|
||||||
|
setlogmask(LOG_UPTO (LOG_DEBUG));
|
||||||
|
openlog("lwsts", syslog_options, LOG_DAEMON);
|
||||||
|
|
||||||
|
/* tell the library what debug level to emit and to send it to syslog */
|
||||||
|
lws_set_log_level(debug_level, lwsl_emit_syslog);
|
||||||
|
|
||||||
|
info.iface = iface;
|
||||||
|
info.protocols = protocols;
|
||||||
|
info.extensions = libwebsocket_get_internal_extensions();
|
||||||
|
info.ssl_cert_filepath = NULL;
|
||||||
|
info.ssl_private_key_filepath = NULL;
|
||||||
|
|
||||||
|
info.gid = -1;
|
||||||
|
info.uid = -1;
|
||||||
|
info.options = opts;
|
||||||
|
|
||||||
|
context = libwebsocket_create_context(&info);
|
||||||
|
if (context == NULL) {
|
||||||
|
lwsl_err("libwebsocket init failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while (n >= 0 && !force_exit) {
|
||||||
|
|
||||||
|
n = libwebsocket_service(context, 50);
|
||||||
|
|
||||||
|
};//while n>=0
|
||||||
|
|
||||||
|
|
||||||
|
libwebsocket_context_destroy(context);
|
||||||
|
|
||||||
|
lwsl_notice("libwebsockets-test-server exited cleanly\n");
|
||||||
|
|
||||||
|
closelog();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}//main
|
||||||
106
Trinamic/websock_simple/test.html
Normal file
106
Trinamic/websock_simple/test.html
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset=koi8-r http-equiv="Content-Language" content="ru"/>
|
||||||
|
<title>A test</title>
|
||||||
|
<style type="text/css">
|
||||||
|
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; border-radius:10px; }
|
||||||
|
button { display: block; width: 70px; text-align: center; }
|
||||||
|
.container {border: solid 1px; border-radius:10px; padding:12px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body onload="Global.init()">
|
||||||
|
<div class="container" id="cont">
|
||||||
|
<table class="content" id="tab">
|
||||||
|
<tr><td></td>
|
||||||
|
<td align='center'><button id='Y+'>Y+</button></td>
|
||||||
|
<td></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td align='center'><button id='X-'>X-</button></td>
|
||||||
|
<td></td>
|
||||||
|
<td align='center'><button id='X+'>X+</button></td>
|
||||||
|
</tr>
|
||||||
|
<tr><td></td>
|
||||||
|
<td align='center'><button id='Y-'>Y-</button></td>
|
||||||
|
<td></td></tr>
|
||||||
|
</table>
|
||||||
|
<p></p>
|
||||||
|
<div id = "connected">No connection</div>
|
||||||
|
<div id = "answer"></div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
Global = function(){
|
||||||
|
var socket = null;
|
||||||
|
var connected = 0;
|
||||||
|
function get_appropriate_ws_url(){
|
||||||
|
var pcol;
|
||||||
|
var u = document.URL;
|
||||||
|
if (u.substring(0, 5) == "https") {
|
||||||
|
pcol = "wss://";
|
||||||
|
u = u.substr(8);
|
||||||
|
} else {
|
||||||
|
pcol = "ws://";
|
||||||
|
if (u.substring(0, 4) == "http")
|
||||||
|
u = u.substr(7);
|
||||||
|
}
|
||||||
|
u = u.split('/');
|
||||||
|
return pcol + u[0] + ":9999";
|
||||||
|
}
|
||||||
|
function TryConnect(){
|
||||||
|
if(connected) return;
|
||||||
|
if(socket) delete socket;
|
||||||
|
if (typeof MozWebSocket != "undefined") {
|
||||||
|
socket = new MozWebSocket(get_appropriate_ws_url(),
|
||||||
|
"XY-protocol");
|
||||||
|
} else {
|
||||||
|
socket = new WebSocket(get_appropriate_ws_url(),
|
||||||
|
"XY-protocol");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
socket.onopen = function(){
|
||||||
|
document.getElementById("connected").style.backgroundColor = "#40ff40";
|
||||||
|
document.getElementById("connected").textContent = "Connection opened";
|
||||||
|
connected = 1;
|
||||||
|
}
|
||||||
|
socket.onmessage = function got_packet(msg) {
|
||||||
|
document.getElementById("answer").textContent = msg.data + "\n";
|
||||||
|
}
|
||||||
|
socket.onclose = function(){
|
||||||
|
document.getElementById("connected").style.backgroundColor = "#ff4040";
|
||||||
|
document.getElementById("connected").textContent = "Connection closed";
|
||||||
|
document.getElementById("answer").textContent = "";
|
||||||
|
connected = 0;
|
||||||
|
setTimeout(TryConnect, 1000);
|
||||||
|
}
|
||||||
|
} catch(exception) {
|
||||||
|
alert('Error' + exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function init(){
|
||||||
|
console.log("init");
|
||||||
|
document.getElementById("cont").style.width = document.getElementById("tab").clientWidth;
|
||||||
|
var Buttons = document.getElementsByTagName("button");
|
||||||
|
for(var i = 0; i < Buttons.length; i++){
|
||||||
|
//Buttons[i].addEventListener("click", btnclick);
|
||||||
|
Buttons[i].addEventListener("mousedown", btnmousedown);
|
||||||
|
Buttons[i].addEventListener("mouseup", btnmouseup);
|
||||||
|
}
|
||||||
|
TryConnect();
|
||||||
|
}
|
||||||
|
/*function btnclick(){
|
||||||
|
console.log("Click: " + this.id);
|
||||||
|
}*/
|
||||||
|
function btnmouseup(){
|
||||||
|
console.log("Mouse up: " + this.id);
|
||||||
|
if(connected) socket.send("U"+this.id);
|
||||||
|
}
|
||||||
|
function btnmousedown(){
|
||||||
|
console.log("Mouse down: " + this.id);
|
||||||
|
if(connected) socket.send("D"+this.id);
|
||||||
|
}
|
||||||
|
return{
|
||||||
|
init: init
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
23
Zernike/Makefile
Normal file
23
Zernike/Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
LOADLIBES = -lm -lgsl -lgslcblas
|
||||||
|
SRCS = zernike.c zernikeR.c zernike_annular.c
|
||||||
|
CC = gcc
|
||||||
|
DEFINES = -D_GNU_SOURCE
|
||||||
|
#-D_XOPEN_SOURCE=501
|
||||||
|
CXX = gcc
|
||||||
|
CFLAGS = -Wall -Werror $(DEFINES)
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
all : zernike btatest
|
||||||
|
zernike : $(OBJS) test.o
|
||||||
|
$(CC) $(CFLAGS) test.o $(OBJS) $(LOADLIBES) -o zernike
|
||||||
|
btatest : $(OBJS) Z-BTA_test.o simple_list.o
|
||||||
|
$(CC) $(CFLAGS) Z-BTA_test.o simple_list.o $(OBJS) $(LOADLIBES) -o btatest
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(SRCS)
|
||||||
|
|
||||||
|
### <DEPENDENCIES ON .h FILES GO HERE>
|
||||||
|
# name1.o : header1.h header2.h ...
|
||||||
|
test.o zernike.o zernikeR.o zernike_annular.o Z-BTA_test.o : zernike.h
|
||||||
|
zernike.o zernikeR.o zernike_annular.o : zern_private.h
|
||||||
|
simple_list.o Z-BTA_test.o : simple_list.h
|
||||||
26
Zernike/README
Normal file
26
Zernike/README
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Here is my realisation of wavefront decomposition and restoration based on
|
||||||
|
Zernike polynomials (up to 100th order).
|
||||||
|
|
||||||
|
The base functions are:
|
||||||
|
|
||||||
|
double *zernfunR(int n, int m, int W, int H, double *norm);
|
||||||
|
calculates Zernike polynomial of order n and angular order m on
|
||||||
|
equidistance rectangular grid WxH (norm is NULL or value to store
|
||||||
|
sum(Z_i^2) for normalisation on decomposition.
|
||||||
|
|
||||||
|
double *zernfunNR(int p, int W, int H, double *norm);
|
||||||
|
the same but in Noll notation
|
||||||
|
|
||||||
|
double *Zdecompose(int Nmax, int W, int H, double *image, int *Zsz, int *lastIdx);
|
||||||
|
decompose image by Zernike polynomials with order <= Nmax,
|
||||||
|
Zsz is size of returned array (in Noll notation)
|
||||||
|
lastidx is last non-zero coefficient
|
||||||
|
|
||||||
|
double *Zcompose(int Zsz, double *Zidxs, int W, int H);
|
||||||
|
build image based on Zernike coeffs Zidxs
|
||||||
|
Zsz - size of array in Noll notation
|
||||||
|
W, H - size of future image
|
||||||
|
|
||||||
|
void set_prec(double val);
|
||||||
|
set precision of Zernike transforms
|
||||||
|
|
||||||
402
Zernike/Z-BTA_test.c
Normal file
402
Zernike/Z-BTA_test.c
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* Z-BTA_test.c - simple test for models of hartmannograms
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <sys/stat.h> // open/close/etc
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h> // mmap
|
||||||
|
|
||||||
|
#include "zernike.h"
|
||||||
|
#include "simple_list.h"
|
||||||
|
|
||||||
|
#define _(...) __VA_ARGS__
|
||||||
|
extern char *__progname;
|
||||||
|
|
||||||
|
#define ERR(...) err(1, __VA_ARGS__)
|
||||||
|
#define ERRX(...) errx(1, __VA_ARGS__)
|
||||||
|
|
||||||
|
// x0 = (*spot)->c.x - AxisX;
|
||||||
|
// y0 = AxisY - (*spot)->c.y;
|
||||||
|
double AxisX = 1523., AxisY = 1533.; // BUG from fitsview : xreal = x + AxisX, yreal = y - AxisY
|
||||||
|
|
||||||
|
char *name0 = NULL, *name1 = NULL; // prefocal/postfocal filenames
|
||||||
|
double distance = -1.; // distance between images
|
||||||
|
double pixsize = 30.; // pixel size
|
||||||
|
double ImHeight = 500.; // half of image height in pixels
|
||||||
|
|
||||||
|
// spots for spotlist
|
||||||
|
typedef struct{
|
||||||
|
int id; // spot identificator
|
||||||
|
double x; // coordinates of center
|
||||||
|
double y;
|
||||||
|
} spot;
|
||||||
|
|
||||||
|
// hartmannogram
|
||||||
|
typedef struct{
|
||||||
|
char *filename; // spots-filename
|
||||||
|
int len; // amount of spots
|
||||||
|
List *spots; // spotlist
|
||||||
|
} hartmann;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print usage with optional message & exit with error(1)
|
||||||
|
* @param fmt ... - printf-like message, no tailing "\n" required!
|
||||||
|
*/
|
||||||
|
void usage(char *fmt, ...){
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
printf("\n");
|
||||||
|
if (fmt != NULL){
|
||||||
|
vprintf(fmt, ap);
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
// "Использование:\t%s опции\n"
|
||||||
|
printf(_("Usage:\t%s options\n"),
|
||||||
|
__progname);
|
||||||
|
// "Опции:\n"
|
||||||
|
printf(_("Required options:\n"));
|
||||||
|
printf("\t--prefocal, -0 <file>\t\tfilename with spotslist in " RED "prefocal" OLDCOLOR " image\n");
|
||||||
|
printf("\t--postfocal,-1 <file>\t\tfilename with spotslist in " RED "postfocal" OLDCOLOR " image\n");
|
||||||
|
printf("\t--distance, -D <distance in mm>\tdistance between images in millimeters\n");
|
||||||
|
printf(_("Unnesessary options:\n"));
|
||||||
|
printf("\t--pixsize, -p <size in mkm>\tpixel size in microns (default: 30)\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* converts string into double number
|
||||||
|
* @param num (o) - result of conversion
|
||||||
|
* @param str (i) - string with number
|
||||||
|
* @return num of NULL in case of error
|
||||||
|
*/
|
||||||
|
double *myatod(double *num, const char *str){
|
||||||
|
double tmp;
|
||||||
|
char *endptr;
|
||||||
|
if(!num) return NULL;
|
||||||
|
if(!str) return NULL;
|
||||||
|
tmp = strtod(str, &endptr);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return NULL;
|
||||||
|
*num = tmp;
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parce arguments of main() & fill global options
|
||||||
|
*/
|
||||||
|
void parse_args(int argc, char **argv){
|
||||||
|
int i;
|
||||||
|
char short_options[] = "0:1:D:p:"; // all short equivalents
|
||||||
|
struct option long_options[] = {
|
||||||
|
/* { name, has_arg, flag, val }, where:
|
||||||
|
* name - name of long parameter
|
||||||
|
* has_arg = 0 - no argument, 1 - need argument, 2 - unnesessary argument
|
||||||
|
* flag = NULL to return val, pointer to int - to set it
|
||||||
|
* value of val (function returns 0)
|
||||||
|
* val - getopt_long return value or value, flag setting to
|
||||||
|
* !!! last string - for zeros !!!
|
||||||
|
*/
|
||||||
|
{"prefocal", 1, 0, '0'},
|
||||||
|
{"postfocal", 1, 0, '1'},
|
||||||
|
{"distance", 1, 0, 'D'},
|
||||||
|
{"pixsize", 1, 0, '0'},
|
||||||
|
{ 0, 0, 0, 0 }
|
||||||
|
};
|
||||||
|
if(argc == 1){
|
||||||
|
usage(_("Parameters required"));
|
||||||
|
}
|
||||||
|
while(1){
|
||||||
|
int opt;
|
||||||
|
if((opt = getopt_long(argc, argv, short_options,
|
||||||
|
long_options, NULL)) == -1) break;
|
||||||
|
switch(opt){
|
||||||
|
/*case 0: // only long option
|
||||||
|
// do something?
|
||||||
|
break;*/
|
||||||
|
case '0':
|
||||||
|
name0 = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
name1 = strdup(optarg);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if(!myatod(&distance, optarg))
|
||||||
|
usage("Parameter <distance> should be a floating point number!");
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if(!myatod(&pixsize, optarg))
|
||||||
|
usage("Parameter <pixsize> should be an int or floating point number!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if(argc > 0){
|
||||||
|
// "Игнорирую аргумент[ы]:\n"
|
||||||
|
printf(_("Ignore argument[s]:\n"));
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
printf("%s ", argv[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
if(!name0 || !name1)
|
||||||
|
usage("You should point to both spots-files");
|
||||||
|
if(distance < 0.)
|
||||||
|
usage("What is the distance between images?");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
} mmapbuf;
|
||||||
|
/**
|
||||||
|
* Mmap file to a memory area
|
||||||
|
*
|
||||||
|
* @param filename (i) - name of file to mmap
|
||||||
|
* @return stuct with mmap'ed file or die
|
||||||
|
*/
|
||||||
|
mmapbuf *My_mmap(char *filename){
|
||||||
|
int fd;
|
||||||
|
char *ptr;
|
||||||
|
size_t Mlen;
|
||||||
|
struct stat statbuf;
|
||||||
|
if(!filename) ERRX(_("No filename given!"));
|
||||||
|
if((fd = open(filename, O_RDONLY)) < 0)
|
||||||
|
ERR(_("Can't open %s for reading"), filename);
|
||||||
|
if(fstat (fd, &statbuf) < 0)
|
||||||
|
ERR(_("Can't stat %s"), filename);
|
||||||
|
Mlen = statbuf.st_size;
|
||||||
|
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
|
||||||
|
ERR(_("Mmap error for input"));
|
||||||
|
if(close(fd)) ERR(_("Can't close mmap'ed file"));
|
||||||
|
mmapbuf *ret = MALLOC(mmapbuf, 1);
|
||||||
|
ret->data = ptr;
|
||||||
|
ret->len = Mlen;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void My_munmap(mmapbuf *b){
|
||||||
|
if(munmap(b->data, b->len))
|
||||||
|
ERR(_("Can't munmap"));
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read spots-file and fill hartmann structure
|
||||||
|
* @param filename - name of spots-file
|
||||||
|
* @return dynamically allocated hartmanogram structure
|
||||||
|
*/
|
||||||
|
hartmann *read_spots(char *filename){
|
||||||
|
assert(filename);
|
||||||
|
mmapbuf *M = NULL;
|
||||||
|
int L = 0;
|
||||||
|
List *spots = NULL, *curspot = NULL;
|
||||||
|
M = My_mmap(filename);
|
||||||
|
hartmann *H = MALLOC(hartmann, 1);
|
||||||
|
H->filename = strdup(filename);
|
||||||
|
char *pos = M->data, *epos = pos + M->len;
|
||||||
|
for(; pos && pos < epos; pos = strchr(pos+1, '\n')){
|
||||||
|
spot *Spot = MALLOC(spot, 1);
|
||||||
|
double x, y;
|
||||||
|
if(3 != sscanf(pos, "%d %*s %*s %*s %*s %lf %lf", &Spot->id, &x, &y))
|
||||||
|
continue;
|
||||||
|
Spot->x = x + AxisX - ImHeight;
|
||||||
|
Spot->y = y - AxisY + ImHeight;
|
||||||
|
L++;
|
||||||
|
if(spots)
|
||||||
|
curspot = list_add(&curspot, LIST_T(Spot));
|
||||||
|
else
|
||||||
|
curspot = list_add(&spots, LIST_T(Spot));
|
||||||
|
};
|
||||||
|
H->len = L;
|
||||||
|
H->spots = spots;
|
||||||
|
My_munmap(M);
|
||||||
|
return H;
|
||||||
|
}
|
||||||
|
|
||||||
|
void h_free(hartmann **H){
|
||||||
|
listfree_function(free);
|
||||||
|
list_free(&((*H)->spots));
|
||||||
|
listfree_function(NULL);
|
||||||
|
free((*H)->filename);
|
||||||
|
FREE(*H);
|
||||||
|
}
|
||||||
|
|
||||||
|
// temporary structure for building of coordinates-gradients list
|
||||||
|
typedef struct{
|
||||||
|
double x;
|
||||||
|
double y;
|
||||||
|
double Dx;
|
||||||
|
double Dy;
|
||||||
|
} CG;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param H (i) - array of thwo hartmannograms (0 - prefocal, 1 - postfocal)
|
||||||
|
* @param coords (o) - array of gradients' coordinates on prefocal image (allocated here) - the same as H[0]->spots coordinates
|
||||||
|
* @param grads (o) - gradients' array (allocated here)
|
||||||
|
* @param scale (o) - scale of polar coordinate R (== Rmax)
|
||||||
|
* @return size of built arrays
|
||||||
|
*/
|
||||||
|
size_t get_gradients(hartmann *H[], polar **coords, point **grads, double *scale){
|
||||||
|
size_t Sz = 0, i;
|
||||||
|
assert(H); assert(H[0]); assert(H[1]);
|
||||||
|
List *S0 = H[0]->spots, *S1;
|
||||||
|
List *CG_list = NULL, *curCG = NULL;
|
||||||
|
//printf(RED "\nspots\n" OLDCOLOR "\n");
|
||||||
|
double Scale = pixsize * 1e-6 / distance / 2., S = 0.; // tg(2a)=dx/D -> a \approx dx/(2D)
|
||||||
|
/*
|
||||||
|
* Both lists have sorted structure
|
||||||
|
* but they can miss some points - that's why we should find exact points.
|
||||||
|
* To store dinamycally data I use List
|
||||||
|
*/
|
||||||
|
for(; S0; S0 = S0->next){
|
||||||
|
spot *Sp0 = (spot*)S0->data;
|
||||||
|
int Id0 = Sp0->id;
|
||||||
|
S1 = H[1]->spots;
|
||||||
|
for(; S1; S1 = S1->next){
|
||||||
|
spot *Sp1 = (spot*)S1->data;
|
||||||
|
if(Sp1->id > Id0) break; // point with Id0 not found
|
||||||
|
if(Sp1->id == Id0){
|
||||||
|
CG *cg = MALLOC(CG, 1);
|
||||||
|
/* printf("id=%d (%g, %g), dx=%g, dy=%g\n", Sp0->id, Sp0->x, Sp0->y,
|
||||||
|
(Sp1->x-Sp0->x)*Scale, (Sp1->y-Sp0->y)*Scale);
|
||||||
|
*/ cg->x = Sp0->x; cg->y = Sp0->y;
|
||||||
|
cg->Dx = -(Sp1->x-Sp0->x)*Scale;
|
||||||
|
cg->Dy = -(Sp1->y-Sp0->y)*Scale;
|
||||||
|
Sz++;
|
||||||
|
if(CG_list)
|
||||||
|
curCG = list_add(&curCG, cg);
|
||||||
|
else
|
||||||
|
curCG = list_add(&CG_list, cg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
polar *C = MALLOC(polar, Sz), *cptr = C;
|
||||||
|
point *G = MALLOC(point, Sz), *gptr = G;
|
||||||
|
curCG = CG_list;
|
||||||
|
for(i = 0; i < Sz; i++, cptr++, gptr++, curCG = curCG->next){
|
||||||
|
double x, y, length, R;
|
||||||
|
CG *cur = (CG*)curCG->data;
|
||||||
|
x = cur->x; y = cur->y;
|
||||||
|
R = sqrt(x*x + y*y);
|
||||||
|
cptr->r = R;
|
||||||
|
if(S < R) S = R; // find max R
|
||||||
|
cptr->theta = atan2(y, x);
|
||||||
|
x = cur->Dx; y = cur->Dy;
|
||||||
|
length = sqrt(1. + x*x + y*y); // length of vector for norm
|
||||||
|
gptr->x = x / length;
|
||||||
|
gptr->y = y / length;
|
||||||
|
}
|
||||||
|
cptr = C;
|
||||||
|
for(i = 0; i < Sz; i++, cptr++)
|
||||||
|
cptr->r /= S;
|
||||||
|
*scale = S;
|
||||||
|
*coords = C; *grads = G;
|
||||||
|
listfree_function(free);
|
||||||
|
list_free(&CG_list);
|
||||||
|
listfree_function(NULL);
|
||||||
|
return Sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
int _U_ i;
|
||||||
|
double scale;
|
||||||
|
hartmann _U_ *images[2];
|
||||||
|
parse_args(argc, argv);
|
||||||
|
images[0] = read_spots(name0);
|
||||||
|
images[1] = read_spots(name1);
|
||||||
|
polar *coords = NULL; point *grads = NULL;
|
||||||
|
size_t _U_ L = get_gradients(images, &coords, &grads, &scale);
|
||||||
|
h_free(&images[0]);
|
||||||
|
h_free(&images[1]);
|
||||||
|
/* printf(GREEN "\nSpots:\n" OLDCOLOR "\n\tr\ttheta\tDx\tDy\n");
|
||||||
|
for(i = 0; i < L; i++){
|
||||||
|
printf("%8.1f%8.4f%8.4f%8.4f\n", coords[i].r, coords[i].theta,
|
||||||
|
grads[i].x, grads[i].y);
|
||||||
|
}
|
||||||
|
*/ int Zsz, lastidx;
|
||||||
|
double *Zidxs = LS_gradZdecomposeR(15, L, coords, grads, &Zsz, &lastidx);
|
||||||
|
lastidx++;
|
||||||
|
printf("\n" RED "GradZ decompose, coefficients (%d):" OLDCOLOR "\n", lastidx);
|
||||||
|
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
|
||||||
|
printf("\n\n");
|
||||||
|
const int GridSize = 15;
|
||||||
|
int G2 = GridSize * GridSize;
|
||||||
|
polar *rect = MALLOC(polar, G2), *rptr = rect;
|
||||||
|
double *mirZ = MALLOC(double, G2);
|
||||||
|
#define ADD 0.
|
||||||
|
int j; double Stp = 2./((double)GridSize - 1.);
|
||||||
|
for(j = 0; j < GridSize; j++){
|
||||||
|
double y = ((double) j + ADD) * Stp - 1.;
|
||||||
|
for(i = 0; i < GridSize; i++, rptr++){
|
||||||
|
double x = ((double) i + ADD)* Stp - 1.;
|
||||||
|
double R2 = x*x + y*y;
|
||||||
|
rptr->r = sqrt(R2);
|
||||||
|
rptr->theta = atan2(y, x);
|
||||||
|
//printf("x=%g, y=%g, r=%g, t=%g\n", x,y,rptr->r, rptr->theta);
|
||||||
|
if(R2 > 1.) continue;
|
||||||
|
mirZ[j*GridSize+i] = R2 / 32.; // mirror surface, z = r^2/(4f), or (z/R) = (r/R)^2/(4[f/R])
|
||||||
|
//mirZ[j*GridSize+i] = R2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n\nCoeff: %g\n\n", Zidxs[4]*sqrt(3.));
|
||||||
|
Zidxs[4] = 0.; Zidxs[1] = 0.; Zidxs[2] = 0.;
|
||||||
|
double *comp = ZcomposeR(lastidx, Zidxs, G2, rect);
|
||||||
|
printf("\n");
|
||||||
|
double SS = 0.; int SScntr = 0;
|
||||||
|
for(j = GridSize-1; j > -1; j--){
|
||||||
|
for(i = 0; i < GridSize; i++){
|
||||||
|
int idx = j*GridSize+i;
|
||||||
|
double Diff = mirZ[idx] - comp[idx];
|
||||||
|
// printf("%7.3f", Diff);
|
||||||
|
printf("%7.3f", comp[idx]*1e3);
|
||||||
|
if(rect[idx].r < 1.){ SS += Diff; SScntr++;}
|
||||||
|
//printf("%7.3f", (comp[idx] + 0.03)/ mirZ[idx]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
SS /= SScntr;
|
||||||
|
printf("\naver: %g\n", SS);
|
||||||
|
for(j = GridSize-1; j > -1; j--){
|
||||||
|
for(i = 0; i < GridSize; i++){
|
||||||
|
int idx = j*GridSize+i;
|
||||||
|
//printf("%7.3f", (comp[idx] + SS) / mirZ[idx]);
|
||||||
|
double Z = (fabs(comp[idx]) < 2*DBL_EPSILON) ? 0. : comp[idx] + SS - mirZ[idx];
|
||||||
|
printf("%7.3f", Z * 1e3);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
FREE(comp); FREE(Zidxs);
|
||||||
|
FREE(coords);
|
||||||
|
FREE(grads);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
112
Zernike/simple_list.c
Normal file
112
Zernike/simple_list.c
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* simple_list.c - simple one-direction list
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#include "simple_list.h"
|
||||||
|
|
||||||
|
#define MALLOC alloc_simple_list
|
||||||
|
/**
|
||||||
|
* Memory allocation with control
|
||||||
|
* @param N - number of elements
|
||||||
|
* @param S - size of one element
|
||||||
|
* @return allocated memory or die
|
||||||
|
*/
|
||||||
|
static void *alloc_simple_list(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (*listdata_free)(void *node_data) = NULL; // external function to free(listdata)
|
||||||
|
|
||||||
|
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add element v to list with root root (also this can be last element)
|
||||||
|
* @param root (io) - pointer to root (or last element) of list or NULL
|
||||||
|
* if *root == NULL, just created node will be placed there
|
||||||
|
* @param v - data inserted
|
||||||
|
* @return pointer to created node
|
||||||
|
*/
|
||||||
|
List *list_add(List **root, void *v){
|
||||||
|
List *node, *last;
|
||||||
|
if((node = (List*) MALLOC(1, sizeof(List))) == 0) return NULL; // allocation error
|
||||||
|
node->data = v; // insert data
|
||||||
|
if(root){
|
||||||
|
if(*root){ // there was root node - search last
|
||||||
|
last = *root;
|
||||||
|
while(last->next) last = last->next;
|
||||||
|
last->next = node; // insert pointer to new node into last element in list
|
||||||
|
}
|
||||||
|
if(!*root) *root = node;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set listdata_free()
|
||||||
|
* @param fn - function that freec memory of (node)
|
||||||
|
*/
|
||||||
|
void listfree_function(void (*fn)(void *node)){
|
||||||
|
listdata_free = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove all nodes in list
|
||||||
|
* @param root - pointer to root node
|
||||||
|
*/
|
||||||
|
void list_free(List **root){
|
||||||
|
List *node = *root, *next;
|
||||||
|
if(!root || !*root) return;
|
||||||
|
do{
|
||||||
|
next = node->next;
|
||||||
|
if(listdata_free)
|
||||||
|
listdata_free(node->data);
|
||||||
|
free(node);
|
||||||
|
node = next;
|
||||||
|
}while(node);
|
||||||
|
*root = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
int main(void) {
|
||||||
|
List *tp = NULL, *root_p = NULL;
|
||||||
|
int i, ins[] = {4,2,6,1,3,4,7};
|
||||||
|
for(i = 0; i < 7; i++){
|
||||||
|
if(!(tp = list_add(&tp, ins[i])))
|
||||||
|
err(1, "Malloc error"); // can't insert
|
||||||
|
if(!root_p) root_p = tp;
|
||||||
|
}
|
||||||
|
tp = root_p;
|
||||||
|
i = 0;
|
||||||
|
do{
|
||||||
|
printf("element %d = %d\n", i++, tp->data);
|
||||||
|
tp = tp->next;
|
||||||
|
}while(tp);
|
||||||
|
list_free(&root_p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
38
Zernike/simple_list.h
Normal file
38
Zernike/simple_list.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* simple_list.h - header file for simple list support
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __SIMPLE_LIST_H__
|
||||||
|
#define __SIMPLE_LIST_H__
|
||||||
|
|
||||||
|
#define LIST_T(x) ((void*) x)
|
||||||
|
|
||||||
|
typedef struct list_{
|
||||||
|
void *data;
|
||||||
|
struct list_ *next;
|
||||||
|
} List;
|
||||||
|
|
||||||
|
// add element v to list with root root (also this can be last element)
|
||||||
|
List *list_add(List **root, void *v);
|
||||||
|
// set listdata_free()
|
||||||
|
void listfree_function(void (*fn)(void *node));
|
||||||
|
void list_free(List **root);
|
||||||
|
|
||||||
|
#endif // __SIMPLE_LIST_H__
|
||||||
404
Zernike/test.c
Normal file
404
Zernike/test.c
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
/*
|
||||||
|
* test.c - test for zernike.c
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "zernike.h"
|
||||||
|
/**/
|
||||||
|
double IdxsOri[] = {2., // смещение, 0
|
||||||
|
1.1, -0.8, // наклон, 1-2
|
||||||
|
5.5, -3.2, 0., // астигматизм, дефокус, аст., 3-5
|
||||||
|
6.8, 5.5, 0., 0.,// трилистник, кома, 6-9
|
||||||
|
0., 0., 3.3, 1.4, 8.}; // 10-14
|
||||||
|
/**
|
||||||
|
double IdxsOri[] = {1., // смещение, 0
|
||||||
|
1., 1., // наклон, 1-2
|
||||||
|
1., 1., 0., // астигматизм, дефокус, аст., 3-5
|
||||||
|
1., 1., 0., 0.,// трилистник, кома, 6-9
|
||||||
|
0., 0., 1., 1., 1.}; // 10-14
|
||||||
|
**/
|
||||||
|
const int ORI_SZ = 15;
|
||||||
|
const int W = 16, H = 16, Pmax = 8;
|
||||||
|
|
||||||
|
//#define PLOT_
|
||||||
|
|
||||||
|
#define RAD 57.2957795130823
|
||||||
|
#define D2R(x) ((x) / RAD)
|
||||||
|
#define R2D(x) ((x) * RAD)
|
||||||
|
|
||||||
|
void plotRD(double *A, double *A1, int W, int H){
|
||||||
|
int i,j;
|
||||||
|
double S = 0., N = 0.;
|
||||||
|
printf("\n\nRemaining abs. differences:\n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++){
|
||||||
|
double d = fabs(A1[j*W+i] - A[j*W+i]);
|
||||||
|
if(d > DBL_EPSILON){
|
||||||
|
N++;
|
||||||
|
S += d;
|
||||||
|
}
|
||||||
|
printf("%5.2f ", d);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
double dA = S/N;
|
||||||
|
printf("average abs difference: %g\n", dA);
|
||||||
|
if(dA < 1.) return;
|
||||||
|
printf("Corrected diff:\n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++){
|
||||||
|
double X = 0.;
|
||||||
|
if(fabs(A1[j*W+i]) > DBL_EPSILON) // X = A1[j*W+i]+dA;
|
||||||
|
X = fabs(A1[j*W+i] - A[j*W+i])-dA;
|
||||||
|
printf("%6.2f ", X);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
int i, j, lastidx;
|
||||||
|
double *Z, *Zidxs;
|
||||||
|
#if 0
|
||||||
|
//double xstart, ystart;
|
||||||
|
double pt = 4. / ((W>H) ? W-1 : H-1);
|
||||||
|
#ifdef PLOT_
|
||||||
|
Z = zernfunN(1, W,H, NULL);
|
||||||
|
printf("\nZernike for square matrix: \n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++)
|
||||||
|
printf("%5.2f ", Z[j*W+i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
FREE(Z);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Z = Zcompose(ORI_SZ, IdxsOri, W, H);
|
||||||
|
#ifdef PLOT_
|
||||||
|
printf("\n\nDecomposition for image: \n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++)
|
||||||
|
printf("%6.2f ", Z[j*W+i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
xstart = (W-1)/2., ystart = (H-1)/2.;
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++){
|
||||||
|
double yy = (j - ystart)*pt, xx = (i - xstart)*pt;
|
||||||
|
if((xx*xx + yy*yy) <= 1.)
|
||||||
|
Z[j*W+i] =
|
||||||
|
//100*(xx*xx*xx*xx*xx-yy*yy*yy);
|
||||||
|
100.*(xx-0.3)*yy+200.*xx*yy*yy+300.*yy*yy*yy*yy;
|
||||||
|
printf("%6.2f ", Z[j*W+i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
Zidxs = Zdecompose(Pmax, W, H, Z, &j, &lastidx);
|
||||||
|
printf("\nCoefficients: ");
|
||||||
|
for(i = 0; i <= lastidx; i++) printf("%5.3f, ", Zidxs[i]);
|
||||||
|
|
||||||
|
double *Zd = Zcompose(j, Zidxs, W, H);
|
||||||
|
#ifdef PLOT_
|
||||||
|
printf("\n\nComposed image:\n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++)
|
||||||
|
printf("%6.2f ", Zd[j*W+i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
plotRD(Z, Zd, W, H);
|
||||||
|
#endif
|
||||||
|
FREE(Zd);
|
||||||
|
|
||||||
|
//W--, H--;
|
||||||
|
point *testArr = MALLOC(point, W*H);
|
||||||
|
|
||||||
|
/*
|
||||||
|
xstart = (W-1)/2., ystart = (H-1)/2.;
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
point *p = &testArr[j*W];
|
||||||
|
for(i = 0; i < W; i++, p++){
|
||||||
|
double yy = (j - ystart)*pt, xx = (i - xstart)*pt;
|
||||||
|
if((xx*xx + yy*yy) <= 1.){
|
||||||
|
//p->x = 500.*xx*xx*xx*xx;
|
||||||
|
//p->y = -300.*yy*yy;
|
||||||
|
p->x = 100.*yy+200.*yy*yy;
|
||||||
|
p->y = 100.*(xx-0.3)+400.*xx*yy+1200.*yy*yy*yy;
|
||||||
|
}
|
||||||
|
printf("(%4.1f,%4.1f) ",p->x, p->y);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
for(j = 1; j < H-1; j++){
|
||||||
|
point *p = &testArr[j*W+1];
|
||||||
|
double *d = &Z[j*W+1];
|
||||||
|
for(i = 1; i < W-1; i++, p++, d++){
|
||||||
|
p->x = (d[1]-d[-1])/pt;
|
||||||
|
p->y = (d[W]-d[-W])/pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef PLOT_
|
||||||
|
printf("\n\nGradients of field\n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
point *p = &testArr[j*W];
|
||||||
|
for(i = 0; i < W; i++, p++)
|
||||||
|
printf("(%4.1f,%4.1f) ",p->x, p->y);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//Pmax = 2;
|
||||||
|
double *_Zidxs = gradZdecompose(Pmax, W, H, testArr, &j, &lastidx);
|
||||||
|
printf("\nCoefficients: ");
|
||||||
|
for(i = 0; i <= lastidx; i++) printf("%5.3f, ", _Zidxs[i]);
|
||||||
|
//lastidx = j;
|
||||||
|
point *gZd = gradZcompose(j, _Zidxs, W, H);
|
||||||
|
#ifdef PLOT_
|
||||||
|
printf("\n\nComposed image:\n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
point *p = &gZd[j*W];
|
||||||
|
for(i = 0; i < W; i++, p++)
|
||||||
|
printf("(%4.1f,%4.1f) ",p->x, p->y);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n\nRemaining abs differences:\n");
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
point *p = &gZd[j*W];
|
||||||
|
point *d = &testArr[j*W];
|
||||||
|
for(i = 0; i < W; i++, p++, d++)
|
||||||
|
printf("%5.2f ",sqrt((p->x-d->x)*(p->x-d->x)+(p->y-d->y)*(p->y-d->y)));
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FREE(gZd);
|
||||||
|
FREE(testArr);
|
||||||
|
|
||||||
|
lastidx++;
|
||||||
|
double *ZidxsN = convGradIdxs(_Zidxs, lastidx);
|
||||||
|
printf("\nCoefficients for Zernike:\n i n m Z[i] gradZ[i] S[i] Zori[i]\n");
|
||||||
|
for(i = 0; i < lastidx; i++){
|
||||||
|
int n,m;
|
||||||
|
convert_Zidx(i, &n, &m);
|
||||||
|
printf("%2d%3d%3d%8.1f%8.1f%8.1f", i, n,m, Zidxs[i], ZidxsN[i],_Zidxs[i]);
|
||||||
|
if(i < ORI_SZ) printf("%8.1f", IdxsOri[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
FREE(Zidxs);
|
||||||
|
#ifdef PLOT_
|
||||||
|
printf("\n\nComposed image:\n");
|
||||||
|
Zd = Zcompose(lastidx, ZidxsN, W, H);
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
for(i = 0; i < W; i++)
|
||||||
|
printf("%6.1f ", Zd[j*W+i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
plotRD(Z, Zd, W, H);
|
||||||
|
FREE(Zd);
|
||||||
|
#endif
|
||||||
|
FREE(_Zidxs); FREE(ZidxsN);
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
int Sz = 256;
|
||||||
|
double dTh = D2R(360./32);
|
||||||
|
polar *P = MALLOC(polar, Sz), *Pptr = P;
|
||||||
|
void print256(double *Par){
|
||||||
|
for(j = 0; j < 32; j++){
|
||||||
|
for(i = 0; i < 8; i++) printf("%6.1f", Par[i*32+j]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double Z_PREC = get_prec();
|
||||||
|
void print256diff(double *Ori, double *App){
|
||||||
|
printf(RED "Difference" OLDCOLOR " from original in percents:\t\t\t\t\tabs:\n");
|
||||||
|
for(j = 0; j < 32; j++){
|
||||||
|
for(i = 0; i < 8; i++){
|
||||||
|
double den = Ori[i*32+j];
|
||||||
|
if(fabs(den) > Z_PREC)
|
||||||
|
printf("%6.0f", fabs(App[i*32+j]/den-1.)*100.);
|
||||||
|
else
|
||||||
|
printf(" /0 ");
|
||||||
|
}
|
||||||
|
printf("\t");
|
||||||
|
for(i = 0; i < 8; i++)
|
||||||
|
printf("%7.3f", fabs(App[i*32+j]-Ori[i*32+j]));
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void print_std(double *p1, double *p2, int sz){
|
||||||
|
int i;
|
||||||
|
double d = 0., d2 = 0., dmax = 0.;
|
||||||
|
for(i = 0; i < sz; i++, p1++, p2++){
|
||||||
|
double delta = (*p2) - (*p1);
|
||||||
|
d += delta;
|
||||||
|
d2 += delta*delta;
|
||||||
|
double m = fabs(delta);
|
||||||
|
if(m > dmax) dmax = m;
|
||||||
|
}
|
||||||
|
d /= sz;
|
||||||
|
printf("\n" GREEN "Std: %g" OLDCOLOR ", max abs diff: %g\n\n", (d2/sz - d*d), dmax);
|
||||||
|
}
|
||||||
|
void print_idx_diff(double *idx){
|
||||||
|
int i;
|
||||||
|
double *I = idx;
|
||||||
|
//printf("\nDifferences of indexes (i-i0 / i/i0):\n");
|
||||||
|
printf("\nidx (app / real):\n");
|
||||||
|
for(i = 0; i < ORI_SZ; i++, I++)
|
||||||
|
printf("%d: (%3.1f / %3.1f), ", i, *I, IdxsOri[i]);
|
||||||
|
//printf("%d: (%3.1f / %3.1f), ", i, *I - IdxsOri[i], *I/IdxsOri[i]);
|
||||||
|
print_std(idx, IdxsOri, ORI_SZ);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
void mktest__(double* (*decomp_fn)(int, int, polar*, double*, int*, int*), char *msg){
|
||||||
|
Zidxs = decomp_fn(Pmax, Sz, P, Z, &j, &lastidx);
|
||||||
|
printf("\n" RED "%s, coefficients (%d):" OLDCOLOR "\n", msg, lastidx);
|
||||||
|
lastidx++;
|
||||||
|
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
|
||||||
|
printf("\nComposing: %s(%d, Zidxs, %d, P)\n", msg, lastidx, Sz);
|
||||||
|
double *comp = ZcomposeR(lastidx, Zidxs, Sz, P);
|
||||||
|
print_std(Z, comp, Sz);
|
||||||
|
print_idx_diff(Zidxs);
|
||||||
|
print256diff(Z, comp);
|
||||||
|
FREE(comp);
|
||||||
|
FREE(Zidxs);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#define mktest(a) mktest__(a, #a)
|
||||||
|
double R_holes[] = {.175, .247, .295, .340, .379, .414, .448, .478};
|
||||||
|
for(i = 0; i < 8; i++){
|
||||||
|
// double RR = (double)i * 0.1 + 0.3;
|
||||||
|
// double RR = (double)i * 0.14+0.001;
|
||||||
|
// double RR = (double)i * 0.07 + 0.5;
|
||||||
|
double RR = R_holes[i]; // / 0.5;
|
||||||
|
double Th = 0.;
|
||||||
|
for(j = 0; j < 32; j++, Pptr++, Th += dTh){
|
||||||
|
Pptr->r = RR;
|
||||||
|
Pptr->theta = Th;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Z = ZcomposeR(ORI_SZ, IdxsOri, Sz, P);
|
||||||
|
printf(RED "Original:" OLDCOLOR "\n");
|
||||||
|
print256(Z);
|
||||||
|
|
||||||
|
mktest(ZdecomposeR);
|
||||||
|
mktest(QR_decompose);
|
||||||
|
mktest(LS_decompose);
|
||||||
|
|
||||||
|
|
||||||
|
// ann_Z(4, Sz, P, NULL);
|
||||||
|
//polar P1[] = {{0.2, 0.}, {0.6, M_PI_2}, {0.1, M_PI}, {0.92, 3.*M_PI_2}};
|
||||||
|
//ann_Z(8, 4, P1, NULL);
|
||||||
|
/*
|
||||||
|
double id_ann[] = {1.,2.,0.1,-1.,0.5};
|
||||||
|
Z = ann_Zcompose(5, id_ann, Sz, P);
|
||||||
|
*/
|
||||||
|
FREE(Z);
|
||||||
|
Z = ann_Zcompose(ORI_SZ, IdxsOri, Sz, P);
|
||||||
|
printf(RED "Annular:" OLDCOLOR "\n");
|
||||||
|
print256(Z);
|
||||||
|
Zidxs = ann_Zdecompose(7, Sz, P, Z, &j, &lastidx);
|
||||||
|
printf("\nann_Zdecompose, coefficients:\n");
|
||||||
|
for(i = 0; i <= lastidx; i++) printf("%5.3f, ", Zidxs[i]);
|
||||||
|
double *comp = ann_Zcompose(lastidx, Zidxs, Sz, P);
|
||||||
|
print_std(Z, comp, Sz);
|
||||||
|
print_idx_diff(Zidxs);
|
||||||
|
print256diff(Z, comp);
|
||||||
|
FREE(comp);
|
||||||
|
FREE(Zidxs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEST for gradients on hartmann's net
|
||||||
|
*/
|
||||||
|
|
||||||
|
point *gradZ = MALLOC(point, Sz);
|
||||||
|
Pptr = P;
|
||||||
|
point *dP = gradZ;
|
||||||
|
for(i = 0; i < Sz; i++, Pptr++, dP++){
|
||||||
|
double x = Pptr->r * cos(Pptr->theta), y = Pptr->r * sin(Pptr->theta);
|
||||||
|
double x2 _U_ = x*x, x3 _U_ = x*x2;
|
||||||
|
double y2 _U_ = y*y, y3 _U_ = y*y2;
|
||||||
|
double sx, cx;
|
||||||
|
sincos(x, &sx, &cx);
|
||||||
|
|
||||||
|
double val = 1000.*x3 + y2 + 30.*cx*y3;
|
||||||
|
Z[i] = val; val *= 0.05;
|
||||||
|
dP->x = 3000.*x2 - 30.*sx*y3 + val * drand48();
|
||||||
|
dP->y = 2.*y + 90.*cx*y2 + val * drand48();
|
||||||
|
/*
|
||||||
|
double val = 50.*x3*y3 + 3.*x2*y2 - 2.*x*y3 - 8.*x3*y + 7.*x*y;
|
||||||
|
Z[i] = val; val *= 0.05; // 5% from value
|
||||||
|
dP->x = 150.*x2*y3 + 6.*x*y2 - 2.*y3 - 24.*x2*y + 7.*y + val * drand48(); // + error 5%
|
||||||
|
dP->y = 150.*x3*y2 + 6.*x2*y - 6.*x*y2 - 8.*x3 + 7.*x + val * drand48();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
printf("\n" RED "Z cubic:" OLDCOLOR "\n");
|
||||||
|
print256(Z);
|
||||||
|
mktest(ZdecomposeR);
|
||||||
|
mktest(LS_decompose);
|
||||||
|
mktest(QR_decompose);
|
||||||
|
|
||||||
|
Zidxs = LS_gradZdecomposeR(Pmax+2, Sz, P, gradZ, &i, &lastidx);
|
||||||
|
lastidx++;
|
||||||
|
printf("\n" RED "GradZ decompose, coefficients (%d):" OLDCOLOR "\n", lastidx);
|
||||||
|
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
|
||||||
|
comp = ZcomposeR(lastidx, Zidxs, Sz, P);
|
||||||
|
double averD = 0.;
|
||||||
|
for(i = 0; i < Sz; i++) averD += Z[i] - comp[i];
|
||||||
|
averD /= (double)Sz;
|
||||||
|
printf("Z0 = %g\n", averD);
|
||||||
|
for(i = 0; i < Sz; i++) comp[i] += averD;
|
||||||
|
print256diff(Z, comp);
|
||||||
|
FREE(comp);
|
||||||
|
FREE(Zidxs);
|
||||||
|
|
||||||
|
Zidxs = gradZdecomposeR(Pmax+2, Sz, P, gradZ, &i, &lastidx);
|
||||||
|
/*
|
||||||
|
point *gZd = gradZcomposeR(lastidx, Zidxs, Sz, P);
|
||||||
|
printf("\n\nRemaining abs differences:\n");
|
||||||
|
for(i = 0; i < Sz; i++){
|
||||||
|
point p = gZd[i];
|
||||||
|
point d = gradZ[i];
|
||||||
|
printf("%5.2f ",sqrt((p.x-d.x)*(p.x-d.x)+(p.y-d.y)*(p.y-d.y)));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
FREE(gZd);
|
||||||
|
*/
|
||||||
|
// double *ZidxsN = convGradIdxs(Zidxs, lastidx);
|
||||||
|
// FREE(Zidxs); Zidxs = ZidxsN;
|
||||||
|
lastidx++;
|
||||||
|
printf("\n" RED "GradZ decompose, coefficients (%d):" OLDCOLOR "\n", lastidx);
|
||||||
|
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
|
||||||
|
comp = ZcomposeR(lastidx, Zidxs, Sz, P);
|
||||||
|
averD = 0.;
|
||||||
|
for(i = 0; i < Sz; i++) averD += Z[i] - comp[i];
|
||||||
|
averD /= (double)Sz;
|
||||||
|
printf("Z0 = %g\n", averD);
|
||||||
|
for(i = 0; i < Sz; i++) comp[i] += averD;
|
||||||
|
print256diff(Z, comp);
|
||||||
|
FREE(comp);
|
||||||
|
FREE(Zidxs);
|
||||||
|
|
||||||
|
FREE(Z);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
52
Zernike/zern_private.h
Normal file
52
Zernike/zern_private.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* zern_private.h - private variables for zernike.c & zernikeR.c
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __ZERN_PRIVATE_H__
|
||||||
|
#define __ZERN_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef iabs
|
||||||
|
#define iabs(a) (((a)<(0)) ? (-a) : (a))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DBG
|
||||||
|
#define DBG(...) do{fprintf(stderr, __VA_ARGS__); }while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern double *FK;
|
||||||
|
extern double Z_prec;
|
||||||
|
|
||||||
|
// zernike.c
|
||||||
|
void build_factorial();
|
||||||
|
void free_rpow(double ***Rpow, int n);
|
||||||
|
void build_rpow(int W, int H, int n, double **Rad, double ***Rad_pow);
|
||||||
|
double **build_rpowR(int n, int Sz, polar *P);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// zernike_annular.c
|
||||||
|
polar *conv_r(polar *r0, int Sz);
|
||||||
|
|
||||||
|
#endif // __ZERN_PRIVATE_H__
|
||||||
515
Zernike/zernike.c
Normal file
515
Zernike/zernike.c
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
/*
|
||||||
|
* zernike.c - wavefront decomposition using Zernike polynomials
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Literature:
|
||||||
|
@ARTICLE{2007OExpr..1518014Z,
|
||||||
|
author = {{Zhao}, C. and {Burge}, J.~H.},
|
||||||
|
title = "{Orthonormal vector polynomials in a unit circle Part I: basis set derived from gradients of Zernike polynomials}",
|
||||||
|
journal = {Optics Express},
|
||||||
|
year = 2007,
|
||||||
|
volume = 15,
|
||||||
|
pages = {18014},
|
||||||
|
doi = {10.1364/OE.15.018014},
|
||||||
|
adsurl = {http://adsabs.harvard.edu/abs/2007OExpr..1518014Z},
|
||||||
|
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
|
||||||
|
}
|
||||||
|
@ARTICLE{2008OExpr..16.6586Z,
|
||||||
|
author = {{Zhao}, C. and {Burge}, J.~H.},
|
||||||
|
title = "{Orthonormal vector polynomials in a unit circle, Part II : completing the basis set}",
|
||||||
|
journal = {Optics Express},
|
||||||
|
year = 2008,
|
||||||
|
month = apr,
|
||||||
|
volume = 16,
|
||||||
|
pages = {6586},
|
||||||
|
doi = {10.1364/OE.16.006586},
|
||||||
|
adsurl = {http://adsabs.harvard.edu/abs/2008OExpr..16.6586Z},
|
||||||
|
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
|
||||||
|
}
|
||||||
|
*
|
||||||
|
* !!!ATTENTION!!! Axe Y points upside-down!
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "zernike.h"
|
||||||
|
#include "zern_private.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe memory allocation for macro ALLOC
|
||||||
|
* @param N - number of elements to allocate
|
||||||
|
* @param S - size of single element (typically sizeof)
|
||||||
|
* @return pointer to allocated memory area
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
double *FK = NULL;
|
||||||
|
/**
|
||||||
|
* Build pre-computed array of factorials from 1 to 100
|
||||||
|
*/
|
||||||
|
void build_factorial(){
|
||||||
|
double F = 1.;
|
||||||
|
int i;
|
||||||
|
if(FK) return;
|
||||||
|
FK = MALLOC(double, 100);
|
||||||
|
FK[0] = 1.;
|
||||||
|
for(i = 1; i < 100; i++)
|
||||||
|
FK[i] = (F *= (double)i);
|
||||||
|
}
|
||||||
|
|
||||||
|
double Z_prec = 1e-6; // precision of Zernike coefficients
|
||||||
|
/**
|
||||||
|
* Set precision of Zernice coefficients decomposition
|
||||||
|
* @param val - new precision value
|
||||||
|
*/
|
||||||
|
void set_prec(double val){
|
||||||
|
Z_prec = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_prec(){
|
||||||
|
return Z_prec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert polynomial order in Noll notation into n/m
|
||||||
|
* @param p (i) - order of Zernike polynomial in Noll notation
|
||||||
|
* @param N (o) - order of polynomial
|
||||||
|
* @param M (o) - angular parameter
|
||||||
|
*/
|
||||||
|
void convert_Zidx(int p, int *N, int *M){
|
||||||
|
int n = (int) floor((-1.+sqrt(1.+8.*p)) / 2.);
|
||||||
|
*M = (int)(2.0*(p - n*(n+1.)/2. - 0.5*(double)n));
|
||||||
|
*N = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free array of R powers with power n
|
||||||
|
* @param Rpow (i) - array to free
|
||||||
|
* @param n - power of Zernike polinomial for that array (array size = n+1)
|
||||||
|
*/
|
||||||
|
void free_rpow(double ***Rpow, int n){
|
||||||
|
int i, N = n+1;
|
||||||
|
for(i = 0; i < N; i++) FREE((*Rpow)[i]);
|
||||||
|
FREE(*Rpow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build two arrays: with R and its powers (from 0 to n inclusive)
|
||||||
|
* for cartesian quoter I of matrix with size WxH
|
||||||
|
* @param W, H - size of initial matrix
|
||||||
|
* @param n - power of Zernike polinomial (array size = n+1)
|
||||||
|
* @param Rad (o) - NULL or array with R in quater I
|
||||||
|
* @param Rad_pow (o) - NULL or array with powers of R
|
||||||
|
*/
|
||||||
|
void build_rpow(int W, int H, int n, double **Rad, double ***Rad_pow){
|
||||||
|
double Rnorm = fmax((W - 1.) / 2., (H - 1.) / 2.);
|
||||||
|
int i,j, k, N = n+1, w = (W+1)/2, h = (H+1)/2, S = w*h;
|
||||||
|
double **Rpow = MALLOC(double*, N); // powers of R
|
||||||
|
Rpow[0] = MALLOC(double, S);
|
||||||
|
for(j = 0; j < S; j++) Rpow[0][j] = 1.; // zero's power
|
||||||
|
double *R = MALLOC(double, S);
|
||||||
|
// first - fill array of R
|
||||||
|
double xstart = (W%2) ? 0. : 0.5, ystart = (H%2) ? 0. : 0.5;
|
||||||
|
for(j = 0; j < h; j++){
|
||||||
|
double *pt = &R[j*w], x, ww = w;
|
||||||
|
for(x = xstart; x < ww; x++, pt++){
|
||||||
|
double yy = (j + ystart)/Rnorm, xx = x/Rnorm;
|
||||||
|
*pt = sqrt(xx*xx+yy*yy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = 1; i < N; i++){ // Rpow - is quater I of cartesian coordinates ('cause other are fully simmetrical)
|
||||||
|
Rpow[i] = MALLOC(double, S);
|
||||||
|
double *rp = Rpow[i], *rpo = Rpow[i-1];
|
||||||
|
for(j = 0; j < h; j++){
|
||||||
|
int idx = j*w;
|
||||||
|
double *pt = &rp[idx], *pto = &rpo[idx], *r = &R[idx];
|
||||||
|
for(k = 0; k < w; k++, pt++, pto++, r++)
|
||||||
|
*pt = (*pto) * (*r); // R^{n+1} = R^n * R
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Rad) *Rad = R;
|
||||||
|
else FREE(R);
|
||||||
|
if(Rad_pow) *Rad_pow = Rpow;
|
||||||
|
else free_rpow(&Rpow, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate value of Zernike polynomial on rectangular matrix WxH pixels
|
||||||
|
* Center of matrix will be zero point
|
||||||
|
* Scale will be set by max(W/2,H/2)
|
||||||
|
* @param n - order of polynomial (max: 100!)
|
||||||
|
* @param m - angular parameter of polynomial
|
||||||
|
* @param W - width of output array
|
||||||
|
* @param H - height of output array
|
||||||
|
* @param norm (o) - (if !NULL) normalize factor
|
||||||
|
* @return array of Zernike polynomials on given matrix
|
||||||
|
*/
|
||||||
|
double *zernfun(int n, int m, int W, int H, double *norm){
|
||||||
|
double Z = 0., *Zarr = NULL;
|
||||||
|
bool erparm = false;
|
||||||
|
if(W < 2 || H < 2)
|
||||||
|
errx(1, "Sizes of matrix must be > 2!");
|
||||||
|
if(n > 100)
|
||||||
|
errx(1, "Order of Zernike polynomial must be <= 100!");
|
||||||
|
if(n < 0) erparm = true;
|
||||||
|
if(n < iabs(m)) erparm = true; // |m| must be <= n
|
||||||
|
int d = n - m;
|
||||||
|
if(d % 2) erparm = true; // n-m must differ by a prod of 2
|
||||||
|
if(erparm)
|
||||||
|
errx(1, "Wrong parameters of Zernike polynomial (%d, %d)", n, m);
|
||||||
|
if(!FK) build_factorial();
|
||||||
|
double Xc = (W - 1.) / 2., Yc = (H - 1.) / 2.; // coordinate of circle's middle
|
||||||
|
int i, j, k, m_abs = iabs(m), iup = (n-m_abs)/2, w = (W+1)/2;
|
||||||
|
double *R, **Rpow;
|
||||||
|
build_rpow(W, H, n, &R, &Rpow);
|
||||||
|
int SS = W * H;
|
||||||
|
double ZSum = 0.;
|
||||||
|
// now fill output matrix
|
||||||
|
Zarr = MALLOC(double, SS); // output matrix W*H pixels
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
double *Zptr = &Zarr[j*W];
|
||||||
|
double Ryd = fabs(j - Yc);
|
||||||
|
int Ry = w * (int)Ryd; // Y coordinate on R matrix
|
||||||
|
for(i = 0; i < W; i++, Zptr++){
|
||||||
|
Z = 0.;
|
||||||
|
double Rxd = fabs(i - Xc);
|
||||||
|
int Ridx = Ry + (int)Rxd; // coordinate on R matrix
|
||||||
|
if(R[Ridx] > 1.) continue; // throw out points with R>1
|
||||||
|
// calculate R_n^m
|
||||||
|
for(k = 0; k <= iup; k++){ // Sum
|
||||||
|
double z = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
|
||||||
|
/(//----------------------------------- ----- -------------------------------
|
||||||
|
FK[k]*FK[(n+m_abs)/2-k]*FK[(n-m_abs)/2-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
|
||||||
|
);
|
||||||
|
Z += z * Rpow[n-2*k][Ridx]; // *R^{n-2k}
|
||||||
|
}
|
||||||
|
// normalize
|
||||||
|
double eps_m = (m) ? 1. : 2.;
|
||||||
|
Z *= sqrt(2.*(n+1.) / M_PI / eps_m);
|
||||||
|
double theta = atan2(j - Yc, i - Xc);
|
||||||
|
// multiply to angular function:
|
||||||
|
if(m){
|
||||||
|
if(m > 0)
|
||||||
|
Z *= cos(theta*(double)m_abs);
|
||||||
|
else
|
||||||
|
Z *= sin(theta*(double)m_abs);
|
||||||
|
}
|
||||||
|
*Zptr = Z;
|
||||||
|
ZSum += Z*Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(norm) *norm = ZSum;
|
||||||
|
// free unneeded memory
|
||||||
|
FREE(R);
|
||||||
|
free_rpow(&Rpow, n);
|
||||||
|
return Zarr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike polynomials in Noll notation for square matrix
|
||||||
|
* @param p - index of polynomial
|
||||||
|
* @other params are like in zernfun
|
||||||
|
* @return zernfun
|
||||||
|
*/
|
||||||
|
double *zernfunN(int p, int W, int H, double *norm){
|
||||||
|
int n, m;
|
||||||
|
convert_Zidx(p, &n, &m);
|
||||||
|
return zernfun(n,m,W,H,norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike decomposition of image 'image' WxH pixels
|
||||||
|
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
|
||||||
|
* @param W, H (i) - size of image
|
||||||
|
* @param image(i) - image itself
|
||||||
|
* @param Zsz (o) - size of Z coefficients array
|
||||||
|
* @param lastIdx (o) - (if !NULL) last non-zero coefficient
|
||||||
|
* @return array of Zernike coefficients
|
||||||
|
*/
|
||||||
|
double *Zdecompose(int Nmax, int W, int H, double *image, int *Zsz, int *lastIdx){
|
||||||
|
int i, SS = W*H, pmax, maxIdx = 0;
|
||||||
|
double *Zidxs = NULL, *icopy = NULL;
|
||||||
|
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
Zidxs = MALLOC(double, pmax);
|
||||||
|
icopy = MALLOC(double, W*H);
|
||||||
|
memcpy(icopy, image, W*H*sizeof(double)); // make image copy to leave it unchanged
|
||||||
|
*Zsz = pmax;
|
||||||
|
for(i = 0; i < pmax; i++){ // now we fill array
|
||||||
|
double norm, *Zcoeff = zernfunN(i, W, H, &norm);
|
||||||
|
int j;
|
||||||
|
double *iptr = icopy, *zptr = Zcoeff, K = 0.;
|
||||||
|
for(j = 0; j < SS; j++, iptr++, zptr++)
|
||||||
|
K += (*zptr) * (*iptr) / norm; // multiply matrixes to get coefficient
|
||||||
|
Zidxs[i] = K;
|
||||||
|
if(fabs(K) < Z_prec){
|
||||||
|
Zidxs[i] = 0.;
|
||||||
|
continue; // there's no need to substract values that are less than our precision
|
||||||
|
}
|
||||||
|
maxIdx = i;
|
||||||
|
iptr = icopy; zptr = Zcoeff;
|
||||||
|
for(j = 0; j < SS; j++, iptr++, zptr++)
|
||||||
|
*iptr -= K * (*zptr); // subtract composed coefficient to reduce high orders values
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
FREE(icopy);
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike restoration of image WxH pixels by Zernike polynomials coefficients
|
||||||
|
* @param Zsz (i) - number of elements in coefficients array
|
||||||
|
* @param Zidxs(i) - array with Zernike coefficients
|
||||||
|
* @param W, H (i) - size of image
|
||||||
|
* @return restored image
|
||||||
|
*/
|
||||||
|
double *Zcompose(int Zsz, double *Zidxs, int W, int H){
|
||||||
|
int i, SS = W*H;
|
||||||
|
double *image = MALLOC(double, SS);
|
||||||
|
for(i = 0; i < Zsz; i++){ // now we fill array
|
||||||
|
double K = Zidxs[i];
|
||||||
|
if(fabs(K) < Z_prec) continue;
|
||||||
|
double *Zcoeff = zernfunN(i, W, H, NULL);
|
||||||
|
int j;
|
||||||
|
double *iptr = image, *zptr = Zcoeff;
|
||||||
|
for(j = 0; j < SS; j++, iptr++, zptr++)
|
||||||
|
*iptr += K * (*zptr); // add next Zernike polynomial
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components of Zj gradient without constant factor
|
||||||
|
* all parameters are as in zernfun
|
||||||
|
* @return array of gradient's components
|
||||||
|
*/
|
||||||
|
point *gradZ(int n, int m, int W, int H, double *norm){
|
||||||
|
point *gZ = NULL;
|
||||||
|
bool erparm = false;
|
||||||
|
if(W < 2 || H < 2)
|
||||||
|
errx(1, "Sizes of matrix must be > 2!");
|
||||||
|
if(n > 100)
|
||||||
|
errx(1, "Order of gradient of Zernike polynomial must be <= 100!");
|
||||||
|
if(n < 1) erparm = true;
|
||||||
|
if(n < iabs(m)) erparm = true; // |m| must be <= n
|
||||||
|
int d = n - m;
|
||||||
|
if(d % 2) erparm = true; // n-m must differ by a prod of 2
|
||||||
|
if(erparm)
|
||||||
|
errx(1, "Wrong parameters of gradient of Zernike polynomial (%d, %d)", n, m);
|
||||||
|
if(!FK) build_factorial();
|
||||||
|
double Xc = (W - 1.) / 2., Yc = (H - 1.) / 2.; // coordinate of circle's middle
|
||||||
|
int i, j, k, m_abs = iabs(m), iup = (n-m_abs)/2, isum = (n+m_abs)/2, w = (W+1)/2;
|
||||||
|
double *R, **Rpow;
|
||||||
|
build_rpow(W, H, n, &R, &Rpow);
|
||||||
|
int SS = W * H;
|
||||||
|
// now fill output matrix
|
||||||
|
gZ = MALLOC(point, SS);
|
||||||
|
double ZSum = 0.;
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
point *Zptr = &gZ[j*W];
|
||||||
|
double Ryd = fabs(j - Yc);
|
||||||
|
int Ry = w * (int)Ryd; // Y coordinate on R matrix
|
||||||
|
for(i = 0; i < W; i++, Zptr++){
|
||||||
|
double Rxd = fabs(i - Xc);
|
||||||
|
int Ridx = Ry + (int)Rxd; // coordinate on R matrix
|
||||||
|
double Rcurr = R[Ridx];
|
||||||
|
if(Rcurr > 1. || fabs(Rcurr) < DBL_EPSILON) continue; // throw out points with R>1
|
||||||
|
double theta = atan2(j - Yc, i - Xc);
|
||||||
|
// components of grad Zj:
|
||||||
|
|
||||||
|
// 1. Theta_j
|
||||||
|
double Tj = 1., costm, sintm;
|
||||||
|
sincos(theta*(double)m_abs, &sintm, &costm);
|
||||||
|
if(m){
|
||||||
|
if(m > 0)
|
||||||
|
Tj = costm;
|
||||||
|
else
|
||||||
|
Tj = sintm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. dTheta_j/Dtheta
|
||||||
|
double dTj = 0.;
|
||||||
|
if(m){
|
||||||
|
if(m < 0)
|
||||||
|
dTj = m_abs * costm;
|
||||||
|
else
|
||||||
|
dTj = -m_abs * sintm;
|
||||||
|
}
|
||||||
|
// 3. R_j & dR_j/dr
|
||||||
|
double Rj = 0., dRj = 0.;
|
||||||
|
for(k = 0; k <= iup; k++){
|
||||||
|
double rj = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
|
||||||
|
/(//----------------------------------- ----- -------------------------------
|
||||||
|
FK[k]*FK[isum-k]*FK[iup-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
|
||||||
|
);
|
||||||
|
//DBG("rj = %g (n=%d, k=%d)\n",rj, n, k);
|
||||||
|
int kidx = n - 2*k;
|
||||||
|
Rj += rj * Rpow[kidx][Ridx]; // *R^{n-2k}
|
||||||
|
if(kidx > 0)
|
||||||
|
dRj += rj * kidx * Rpow[kidx - 1][Ridx];
|
||||||
|
}
|
||||||
|
// normalisation for Zernike
|
||||||
|
double eps_m = (m) ? 1. : 2., sq = sqrt(2.*(double)(n+1) / M_PI / eps_m);
|
||||||
|
Rj *= sq, dRj *= sq;
|
||||||
|
// 4. sin/cos
|
||||||
|
double sint, cost;
|
||||||
|
sincos(theta, &sint, &cost);
|
||||||
|
|
||||||
|
// projections of gradZj
|
||||||
|
double TdR = Tj * dRj, RdT = Rj * dTj / Rcurr;
|
||||||
|
Zptr->x = TdR * cost - RdT * sint;
|
||||||
|
Zptr->y = TdR * sint + RdT * cost;
|
||||||
|
// norm factor
|
||||||
|
ZSum += Zptr->x * Zptr->x + Zptr->y * Zptr->y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(norm) *norm = ZSum;
|
||||||
|
// free unneeded memory
|
||||||
|
FREE(R);
|
||||||
|
free_rpow(&Rpow, n);
|
||||||
|
return gZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build components of vector polynomial Sj
|
||||||
|
* all parameters are as in zernfunN
|
||||||
|
* @return Sj(n,m) on image points
|
||||||
|
*/
|
||||||
|
point *zerngrad(int p, int W, int H, double *norm){
|
||||||
|
int n, m;
|
||||||
|
convert_Zidx(p, &n, &m);
|
||||||
|
if(n < 1) errx(1, "Order of gradient Z must be > 0!");
|
||||||
|
int m_abs = iabs(m);
|
||||||
|
int i,j;
|
||||||
|
double K = 1., K1 = 1.;///sqrt(2.*n*(n+1.));
|
||||||
|
point *Sj = MALLOC(point, W*H);
|
||||||
|
point *Zj = gradZ(n, m, W, H, NULL);
|
||||||
|
double Zsum = 0.;
|
||||||
|
if(n == m_abs || n < 3){ // trivial case: n = |m| (in case of n=2,m=0 n'=0 -> grad(0,0)=0
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
int idx = j*W;
|
||||||
|
point *Sptr = &Sj[idx], *Zptr = &Zj[idx];
|
||||||
|
for(i = 0; i < W; i++, Sptr++, Zptr++){
|
||||||
|
Sptr->x = K1*Zptr->x;
|
||||||
|
Sptr->y = K1*Zptr->y;
|
||||||
|
Zsum += Sptr->x * Sptr->x + Sptr->y * Sptr->y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
K = sqrt(((double)n+1.)/(n-1.));
|
||||||
|
//K1 /= sqrt(2.);
|
||||||
|
// n != |m|
|
||||||
|
// I run gradZ() twice! But another variant (to make array of Zj) can meet memory lack
|
||||||
|
point *Zj_= gradZ(n-2, m, W, H, NULL);
|
||||||
|
for(j = 0; j < H; j++){
|
||||||
|
int idx = j*W;
|
||||||
|
point *Sptr = &Sj[idx], *Zptr = &Zj[idx], *Z_ptr = &Zj_[idx];
|
||||||
|
for(i = 0; i < W; i++, Sptr++, Zptr++, Z_ptr++){
|
||||||
|
Sptr->x = K1*(Zptr->x - K * Z_ptr->x);
|
||||||
|
Sptr->y = K1*(Zptr->y - K * Z_ptr->y);
|
||||||
|
Zsum += Sptr->x * Sptr->x + Sptr->y * Sptr->y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FREE(Zj_);
|
||||||
|
}
|
||||||
|
FREE(Zj);
|
||||||
|
if(norm) *norm = Zsum;
|
||||||
|
return Sj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decomposition of image with normals to wavefront by Zhao's polynomials
|
||||||
|
* all like Zdecompose
|
||||||
|
* @return array of coefficients
|
||||||
|
*/
|
||||||
|
double *gradZdecompose(int Nmax, int W, int H, point *image, int *Zsz, int *lastIdx){
|
||||||
|
int i, SS = W*H, pmax, maxIdx = 0;
|
||||||
|
double *Zidxs = NULL;
|
||||||
|
point *icopy = NULL;
|
||||||
|
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
Zidxs = MALLOC(double, pmax);
|
||||||
|
icopy = MALLOC(point, SS);
|
||||||
|
memcpy(icopy, image, SS*sizeof(point)); // make image copy to leave it unchanged
|
||||||
|
*Zsz = pmax;
|
||||||
|
for(i = 1; i < pmax; i++){ // now we fill array
|
||||||
|
double norm;
|
||||||
|
point *dZcoeff = zerngrad(i, W, H, &norm);
|
||||||
|
int j;
|
||||||
|
point *iptr = icopy, *zptr = dZcoeff;
|
||||||
|
double K = 0.;
|
||||||
|
for(j = 0; j < SS; j++, iptr++, zptr++)
|
||||||
|
K += (zptr->x*iptr->x + zptr->y*iptr->y) / norm; // multiply matrixes to get coefficient
|
||||||
|
if(fabs(K) < Z_prec)
|
||||||
|
continue; // there's no need to substract values that are less than our precision
|
||||||
|
Zidxs[i] = K;
|
||||||
|
maxIdx = i;
|
||||||
|
iptr = icopy; zptr = dZcoeff;
|
||||||
|
for(j = 0; j < SS; j++, iptr++, zptr++){
|
||||||
|
iptr->x -= K * zptr->x; // subtract composed coefficient to reduce high orders values
|
||||||
|
iptr->y -= K * zptr->y;
|
||||||
|
}
|
||||||
|
FREE(dZcoeff);
|
||||||
|
}
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
FREE(icopy);
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restoration of image with normals Zhao's polynomials coefficients
|
||||||
|
* all like Zcompose
|
||||||
|
* @return restored image
|
||||||
|
*/
|
||||||
|
point *gradZcompose(int Zsz, double *Zidxs, int W, int H){
|
||||||
|
int i, SS = W*H;
|
||||||
|
point *image = MALLOC(point, SS);
|
||||||
|
for(i = 1; i < Zsz; i++){ // now we fill array
|
||||||
|
double K = Zidxs[i];
|
||||||
|
if(fabs(K) < Z_prec) continue;
|
||||||
|
point *Zcoeff = zerngrad(i, W, H, NULL);
|
||||||
|
int j;
|
||||||
|
point *iptr = image, *zptr = Zcoeff;
|
||||||
|
for(j = 0; j < SS; j++, iptr++, zptr++){
|
||||||
|
iptr->x += K * zptr->x;
|
||||||
|
iptr->y += K * zptr->y;
|
||||||
|
}
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
double *convGradIdxs(double *gradIdxs, int Zsz){
|
||||||
|
double *idxNew = MALLOC(double, Zsz);
|
||||||
|
int i;
|
||||||
|
for(i = 1; i < Zsz; i++){
|
||||||
|
int n,m;
|
||||||
|
convert_Zidx(i, &n, &m);
|
||||||
|
int j = ((n+2)*(n+4) + m) / 2;
|
||||||
|
if(j >= Zsz) j = 0;
|
||||||
|
idxNew[i] = (gradIdxs[i] - sqrt((n+3.)/(n+1.))*gradIdxs[j]);
|
||||||
|
}
|
||||||
|
return idxNew;
|
||||||
|
}
|
||||||
92
Zernike/zernike.h
Normal file
92
Zernike/zernike.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* zernike.h
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __ZERNIKE_H__
|
||||||
|
#define __ZERNIKE_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define _U_ __attribute__((unused))
|
||||||
|
#define RED "\033[1;31;40m"
|
||||||
|
#define GREEN "\033[1;32;40m"
|
||||||
|
#define OLDCOLOR "\033[0;0;0m"
|
||||||
|
|
||||||
|
/*************** Data structures & typedefs ***************/
|
||||||
|
// point coordinates
|
||||||
|
typedef struct{
|
||||||
|
double x,y;
|
||||||
|
} point;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
double r,theta;
|
||||||
|
} polar;
|
||||||
|
|
||||||
|
// 2D array
|
||||||
|
typedef struct{
|
||||||
|
double **data;
|
||||||
|
size_t len; // size of 1D arrays
|
||||||
|
size_t num; // number of 1D arrays
|
||||||
|
}_2D;
|
||||||
|
|
||||||
|
#ifndef DBL_EPSILON
|
||||||
|
#define DBL_EPSILON 2.2204460492503131e-16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*************** Memory allocation ***************/
|
||||||
|
#define MALLOC(type, size) ((type*)my_alloc(size, sizeof(type)))
|
||||||
|
#define FREE(ptr) do{free(ptr); ptr = NULL;}while(0)
|
||||||
|
void *my_alloc(size_t N, size_t S);
|
||||||
|
|
||||||
|
/*************** Base functions ***************/
|
||||||
|
void convert_Zidx(int p, int *N, int *M);
|
||||||
|
void set_prec(double val);
|
||||||
|
double get_prec();
|
||||||
|
|
||||||
|
/*************** Zernike on rectangular equidistant coordinate matrix ***************/
|
||||||
|
double *zernfun(int n, int m, int W, int H, double *norm);
|
||||||
|
double *zernfunN(int p, int W, int H, double *norm);
|
||||||
|
|
||||||
|
double *Zdecompose(int Nmax, int W, int H, double *image, int *Zsz, int *lastIdx);
|
||||||
|
double *Zcompose(int Zsz, double *Zidxs, int W, int H);
|
||||||
|
|
||||||
|
double *gradZdecompose(int Nmax, int W, int H, point *image, int *Zsz, int *lastIdx);
|
||||||
|
point *gradZcompose(int Zsz, double *Zidxs, int W, int H);
|
||||||
|
double *convGradIdxs(double *gradIdxs, int Zsz);
|
||||||
|
|
||||||
|
/*************** Zernike on a points set ***************/
|
||||||
|
double *zernfunR(int n, int m, int Sz, polar *P, double *norm);
|
||||||
|
double *zernfunNR(int p, int Sz, polar *P, double *norm);
|
||||||
|
double *ZdecomposeR(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
|
||||||
|
double *ZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P);
|
||||||
|
double *LS_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
|
||||||
|
double *QR_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
|
||||||
|
double *gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx);
|
||||||
|
double *LS_gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx);
|
||||||
|
point *gradZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P);
|
||||||
|
|
||||||
|
/*************** Annular Zernike ***************/
|
||||||
|
_2D *ann_Z(int pmax, int Sz, polar *P, double **Norm);
|
||||||
|
double *ann_Zcompose(int Zsz, double *Zidxs, int Sz, polar *P);
|
||||||
|
double *ann_Zdecompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
|
||||||
|
|
||||||
|
#endif // __ZERNIKE_H__
|
||||||
616
Zernike/zernikeR.c
Normal file
616
Zernike/zernikeR.c
Normal file
@ -0,0 +1,616 @@
|
|||||||
|
/*
|
||||||
|
* zernikeR.c - Zernike decomposition for scattered points & annular aperture
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gsl/gsl_linalg.h>
|
||||||
|
#include <gsl/gsl_matrix.h>
|
||||||
|
#include <gsl/gsl_blas.h>
|
||||||
|
|
||||||
|
#include "zernike.h"
|
||||||
|
#include "zern_private.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build array with R powers (from 0 to n inclusive)
|
||||||
|
* @param n - power of Zernike polinomial (array size = n+1)
|
||||||
|
* @param Sz - size of P array
|
||||||
|
* @param P (i) - polar coordinates of points
|
||||||
|
*/
|
||||||
|
double **build_rpowR(int n, int Sz, polar *P){
|
||||||
|
int i, j, N = n + 1;
|
||||||
|
double **Rpow = MALLOC(double*, N);
|
||||||
|
Rpow[0] = MALLOC(double, Sz);
|
||||||
|
for(i = 0; i < Sz; i++) Rpow[0][i] = 1.; // zero's power
|
||||||
|
for(i = 1; i < N; i++){ // Rpow - is quater I of cartesian coordinates ('cause other are fully simmetrical)
|
||||||
|
Rpow[i] = MALLOC(double, Sz);
|
||||||
|
double *rp = Rpow[i], *rpo = Rpow[i-1];
|
||||||
|
polar *p = P;
|
||||||
|
for(j = 0; j < Sz; j++, rp++, rpo++, p++){
|
||||||
|
*rp = (*rpo) * p->r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Rpow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_parameters(n, m, Sz, P){
|
||||||
|
bool erparm = false;
|
||||||
|
if(Sz < 3 || !P)
|
||||||
|
errx(1, "Size of matrix must be > 2!");
|
||||||
|
if(n > 100)
|
||||||
|
errx(1, "Order of Zernike polynomial must be <= 100!");
|
||||||
|
if(n < 0) erparm = true;
|
||||||
|
if(n < iabs(m)) erparm = true; // |m| must be <= n
|
||||||
|
if((n - m) % 2) erparm = true; // n-m must differ by a prod of 2
|
||||||
|
if(erparm)
|
||||||
|
errx(1, "Wrong parameters of Zernike polynomial (%d, %d)", n, m);
|
||||||
|
else
|
||||||
|
if(!FK) build_factorial();
|
||||||
|
return erparm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike function for scattering data
|
||||||
|
* @param n,m - orders of polynomial
|
||||||
|
* @param Sz - number of points
|
||||||
|
* @param P(i) - array with points coordinates (polar, r<=1)
|
||||||
|
* @param norm(o) - (optional) norm coefficient
|
||||||
|
* @return dynamically allocated array with Z(n,m) for given array P
|
||||||
|
*/
|
||||||
|
double *zernfunR(int n, int m, int Sz, polar *P, double *norm){
|
||||||
|
if(check_parameters(n, m, Sz, P)) return NULL;
|
||||||
|
int j, k, m_abs = iabs(m), iup = (n-m_abs)/2;
|
||||||
|
double **Rpow = build_rpowR(n, Sz, P);
|
||||||
|
double ZSum = 0.;
|
||||||
|
// now fill output matrix
|
||||||
|
double *Zarr = MALLOC(double, Sz); // output matrix
|
||||||
|
double *Zptr = Zarr;
|
||||||
|
polar *p = P;
|
||||||
|
for(j = 0; j < Sz; j++, p++, Zptr++){
|
||||||
|
double Z = 0.;
|
||||||
|
if(p->r > 1.) continue; // throw out points with R>1
|
||||||
|
// calculate R_n^m
|
||||||
|
for(k = 0; k <= iup; k++){ // Sum
|
||||||
|
double z = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
|
||||||
|
/(//----------------------------------- ----- -------------------------------
|
||||||
|
FK[k]*FK[(n+m_abs)/2-k]*FK[(n-m_abs)/2-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
|
||||||
|
);
|
||||||
|
Z += z * Rpow[n-2*k][j]; // *R^{n-2k}
|
||||||
|
}
|
||||||
|
// normalize
|
||||||
|
double eps_m = (m) ? 1. : 2.;
|
||||||
|
Z *= sqrt(2.*(n+1.) / M_PI / eps_m );
|
||||||
|
double m_theta = (double)m_abs * p->theta;
|
||||||
|
// multiply to angular function:
|
||||||
|
if(m){
|
||||||
|
if(m > 0)
|
||||||
|
Z *= cos(m_theta);
|
||||||
|
else
|
||||||
|
Z *= sin(m_theta);
|
||||||
|
}
|
||||||
|
*Zptr = Z;
|
||||||
|
ZSum += Z*Z;
|
||||||
|
}
|
||||||
|
if(norm) *norm = ZSum;
|
||||||
|
// free unneeded memory
|
||||||
|
free_rpow(&Rpow, n);
|
||||||
|
return Zarr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike polynomials in Noll notation for square matrix
|
||||||
|
* @param p - index of polynomial
|
||||||
|
* @other params are like in zernfunR
|
||||||
|
* @return zernfunR
|
||||||
|
*/
|
||||||
|
double *zernfunNR(int p, int Sz, polar *P, double *norm){
|
||||||
|
int n, m;
|
||||||
|
convert_Zidx(p, &n, &m);
|
||||||
|
return zernfunR(n,m,Sz,P,norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike decomposition of image 'image' WxH pixels
|
||||||
|
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
|
||||||
|
* @param Sz, P(i) - size (Sz) of points array (P)
|
||||||
|
* @param heights(i) - wavefront walues in points P
|
||||||
|
* @param Zsz (o) - size of Z coefficients array
|
||||||
|
* @param lastIdx(o) - (if !NULL) last non-zero coefficient
|
||||||
|
* @return array of Zernike coefficients
|
||||||
|
*/
|
||||||
|
double *ZdecomposeR(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
|
||||||
|
int i, pmax, maxIdx = 0;
|
||||||
|
double *Zidxs = NULL, *icopy = NULL;
|
||||||
|
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
Zidxs = MALLOC(double, pmax);
|
||||||
|
icopy = MALLOC(double, Sz);
|
||||||
|
memcpy(icopy, heights, Sz*sizeof(double)); // make image copy to leave it unchanged
|
||||||
|
*Zsz = pmax;
|
||||||
|
for(i = 0; i < pmax; i++){ // now we fill array
|
||||||
|
double norm, *Zcoeff = zernfunNR(i, Sz, P, &norm);
|
||||||
|
int j;
|
||||||
|
double *iptr = icopy, *zptr = Zcoeff, K = 0.;
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++)
|
||||||
|
K += (*zptr) * (*iptr) / norm; // multiply matrixes to get coefficient
|
||||||
|
if(fabs(K) < Z_prec)
|
||||||
|
continue; // there's no need to substract values that are less than our precision
|
||||||
|
Zidxs[i] = K;
|
||||||
|
maxIdx = i;
|
||||||
|
|
||||||
|
iptr = icopy; zptr = Zcoeff;
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++)
|
||||||
|
*iptr -= K * (*zptr); // subtract composed coefficient to reduce high orders values
|
||||||
|
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
FREE(icopy);
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restoration of image in points P by Zernike polynomials coefficients
|
||||||
|
* @param Zsz (i) - number of actual elements in coefficients array
|
||||||
|
* @param Zidxs(i) - array with Zernike coefficients
|
||||||
|
* @param Sz, P(i) - number (Sz) of points (P)
|
||||||
|
* @return restored image
|
||||||
|
*/
|
||||||
|
double *ZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P){
|
||||||
|
int i;
|
||||||
|
double *image = MALLOC(double, Sz);
|
||||||
|
for(i = 0; i < Zsz; i++){ // now we fill array
|
||||||
|
double K = Zidxs[i];
|
||||||
|
if(fabs(K) < Z_prec) continue;
|
||||||
|
double *Zcoeff = zernfunNR(i, Sz, P, NULL);
|
||||||
|
int j;
|
||||||
|
double *iptr = image, *zptr = Zcoeff;
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++)
|
||||||
|
*iptr += K * (*zptr); // add next Zernike polynomial
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints out GSL matrix
|
||||||
|
* @param M (i) - matrix to print
|
||||||
|
*/
|
||||||
|
void print_matrix(gsl_matrix *M){
|
||||||
|
int x,y, H = M->size1, W = M->size2;
|
||||||
|
size_t T = M->tda;
|
||||||
|
printf("\nMatrix %dx%d:\n", W, H);
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
double *ptr = &(M->data[y*T]);
|
||||||
|
printf("str %6d", y);
|
||||||
|
for(x = 0; x < W; x++, ptr++)
|
||||||
|
printf("%6.1f ", *ptr);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
To try less-squares fit I decide after reading of
|
||||||
|
@ARTICLE{2010ApOpt..49.6489M,
|
||||||
|
author = {{Mahajan}, V.~N. and {Aftab}, M.},
|
||||||
|
title = "{Systematic comparison of the use of annular and Zernike circle polynomials for annular wavefronts}",
|
||||||
|
journal = {\ao},
|
||||||
|
year = 2010,
|
||||||
|
month = nov,
|
||||||
|
volume = 49,
|
||||||
|
pages = {6489},
|
||||||
|
doi = {10.1364/AO.49.006489},
|
||||||
|
adsurl = {http://adsabs.harvard.edu/abs/2010ApOpt..49.6489M},
|
||||||
|
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* n'th column of array m is polynomial of n'th degree,
|
||||||
|
* m'th row is m'th data point
|
||||||
|
*
|
||||||
|
* We fill matrix with polynomials by known datapoints coordinates,
|
||||||
|
* after that by less-square fit we get coefficients of decomposition
|
||||||
|
*/
|
||||||
|
double *LS_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
|
||||||
|
int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
/*
|
||||||
|
(from GSL)
|
||||||
|
typedef struct {
|
||||||
|
size_t size1; // rows (height)
|
||||||
|
size_t size2; // columns (width)
|
||||||
|
size_t tda; // data width (aligned) - data stride
|
||||||
|
double * data; // pointer to block->data (matrix data itself)
|
||||||
|
gsl_block * block; // block with matrix data (block->size is size)
|
||||||
|
int owner; // ==1 if matrix owns 'block' (then block will be freed with matrix)
|
||||||
|
} gsl_matrix;
|
||||||
|
*/
|
||||||
|
// Now allocate matrix: its Nth row is equation for Nth data point,
|
||||||
|
// Mth column is Z_M
|
||||||
|
gsl_matrix *M = gsl_matrix_calloc(Sz, pmax);
|
||||||
|
// fill matrix with coefficients
|
||||||
|
int x,y;
|
||||||
|
size_t T = M->tda;
|
||||||
|
for(x = 0; x < pmax; x++){
|
||||||
|
double norm, *Zcoeff = zernfunNR(x, Sz, P, &norm), *Zptr = Zcoeff;
|
||||||
|
double *ptr = &(M->data[x]);
|
||||||
|
for(y = 0; y < Sz; y++, ptr+=T, Zptr++) // fill xth polynomial
|
||||||
|
*ptr = (*Zptr);
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
gsl_vector *ans = gsl_vector_calloc(pmax);
|
||||||
|
|
||||||
|
gsl_vector_view b = gsl_vector_view_array(heights, Sz);
|
||||||
|
gsl_vector *tau = gsl_vector_calloc(pmax); // min(size(M))
|
||||||
|
gsl_linalg_QR_decomp(M, tau);
|
||||||
|
|
||||||
|
gsl_vector *residual = gsl_vector_calloc(Sz);
|
||||||
|
gsl_linalg_QR_lssolve(M, tau, &b.vector, ans, residual);
|
||||||
|
double *ptr = ans->data;
|
||||||
|
int maxIdx = 0;
|
||||||
|
double *Zidxs = MALLOC(double, pmax);
|
||||||
|
for(x = 0; x < pmax; x++, ptr++){
|
||||||
|
if(fabs(*ptr) < Z_prec) continue;
|
||||||
|
Zidxs[x] = *ptr;
|
||||||
|
maxIdx = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsl_matrix_free(M);
|
||||||
|
gsl_vector_free(ans);
|
||||||
|
gsl_vector_free(tau);
|
||||||
|
gsl_vector_free(residual);
|
||||||
|
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pseudo-annular Zernike polynomials
|
||||||
|
* They are just a linear composition of Zernike, made by Gram-Schmidt ortogonalisation
|
||||||
|
* Real Zernike koefficients restored by reverce transformation matrix
|
||||||
|
*
|
||||||
|
* Suppose we have a wavefront data in set of scattered points ${(x,y)}$, we want to
|
||||||
|
* find Zernike coefficients $z$ such that product of Zernike polynomials, $Z$, and
|
||||||
|
* $z$ give us that wavefront data with very little error (depending on $Z$ degree).
|
||||||
|
*
|
||||||
|
* Of cource, $Z$ isn't orthonormal on our basis, so we need to create some intermediate
|
||||||
|
* polynomials, $U$, which will be linear dependent from $Z$ (and this dependency
|
||||||
|
* should be strict and reversable, otherwise we wouldn't be able to reconstruct $z$
|
||||||
|
* from $u$): $U = Zk$. So, we have: $W = Uu + \epsilon$ and $W = Zz + \epsilon$.
|
||||||
|
*
|
||||||
|
* $U$ is orthonormal, so $U^T = U^{-1}$ and (unlike to $Z$) this reverce matrix
|
||||||
|
* exists and is unique. This mean that $u = W^T U = U^T W$.
|
||||||
|
* Coefficients matrix, $k$ must be inversable, so $Uk^{-1} = Z$, this mean that
|
||||||
|
* $z = uk^{-1}$.
|
||||||
|
*
|
||||||
|
* Our main goal is to find that matrix $k$.
|
||||||
|
*
|
||||||
|
* 1. QR-decomposition
|
||||||
|
* In this case non-orthogonal matrix $Z$ decomposed to orthogonal matrix $Q$ and
|
||||||
|
* right-triangular matrix $R$. In our case $Q$ is $U$ itself and $R$ is $k^{-1}$.
|
||||||
|
* QR-decomposition gives us an easy way to compute coefficient's matrix, $k$.
|
||||||
|
* Polynomials in $Q$ are linear-dependent from $Z$, each $n^{th}$ polynomial in $Q$
|
||||||
|
* depends from first $n$ polynomials in $Z$. So, columns of $R$ are coefficients
|
||||||
|
* for making $U$ from $Z$; rows in $R$ are coefficients for making $z$ from $u$.
|
||||||
|
*
|
||||||
|
* 2. Cholesky decomposition
|
||||||
|
* In this case for any non-orthogonal matrix with real values we have:
|
||||||
|
* $A^{T}A = LL^{T}$, where $L$ is left-triangular matrix.
|
||||||
|
* Orthogonal basis made by equation $U = A(L^{-1})^T$. And, as $A = UL^T$, we
|
||||||
|
* can reconstruct coefficients matrix: $k = (L^{-1})^T$.
|
||||||
|
*/
|
||||||
|
|
||||||
|
double *QR_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
|
||||||
|
int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
if(Sz < pmax) errx(1, "Number of points must be >= number of polynomials!");
|
||||||
|
int k, x,y;
|
||||||
|
|
||||||
|
//make new polar
|
||||||
|
polar *Pn = conv_r(P, Sz);
|
||||||
|
// Now allocate matrix: its Nth row is equation for Nth data point,
|
||||||
|
// Mth column is Z_M
|
||||||
|
gsl_matrix *M = gsl_matrix_calloc(Sz, pmax);
|
||||||
|
// Q-matrix (its first pmax columns is our basis)
|
||||||
|
gsl_matrix *Q = gsl_matrix_calloc(Sz, Sz);
|
||||||
|
// R-matrix (coefficients)
|
||||||
|
gsl_matrix *R = gsl_matrix_calloc(Sz, pmax);
|
||||||
|
// fill matrix with coefficients
|
||||||
|
size_t T = M->tda;
|
||||||
|
for(x = 0; x < pmax; x++){
|
||||||
|
double norm, *Zcoeff = zernfunNR(x, Sz, Pn, &norm), *Zptr = Zcoeff;
|
||||||
|
double *ptr = &(M->data[x]);
|
||||||
|
for(y = 0; y < Sz; y++, ptr+=T, Zptr++) // fill xth polynomial
|
||||||
|
*ptr = (*Zptr) / norm;
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
gsl_vector *tau = gsl_vector_calloc(pmax); // min(size(M))
|
||||||
|
gsl_linalg_QR_decomp(M, tau);
|
||||||
|
|
||||||
|
gsl_linalg_QR_unpack(M, tau, Q, R);
|
||||||
|
//print_matrix(R);
|
||||||
|
gsl_matrix_free(M);
|
||||||
|
gsl_vector_free(tau);
|
||||||
|
|
||||||
|
double *Zidxs = MALLOC(double, pmax);
|
||||||
|
T = Q->tda;
|
||||||
|
size_t Tr = R->tda;
|
||||||
|
|
||||||
|
for(k = 0; k < pmax; k++){ // cycle by powers
|
||||||
|
double sumD = 0.;
|
||||||
|
double *Qptr = &(Q->data[k]);
|
||||||
|
for(y = 0; y < Sz; y++, Qptr+=T){ // cycle by points
|
||||||
|
sumD += heights[y] * (*Qptr);
|
||||||
|
}
|
||||||
|
Zidxs[k] = sumD;
|
||||||
|
}
|
||||||
|
gsl_matrix_free(Q);
|
||||||
|
|
||||||
|
// now restore Zernike coefficients
|
||||||
|
double *Zidxs_corr = MALLOC(double, pmax);
|
||||||
|
int maxIdx = 0;
|
||||||
|
for(k = 0; k < pmax; k++){
|
||||||
|
double sum = 0.;
|
||||||
|
double *Rptr = &(R->data[k*(Tr+1)]), *Zptr = &(Zidxs[k]);
|
||||||
|
for(x = k; x < pmax; x++, Zptr++, Rptr++){
|
||||||
|
sum += (*Zptr) * (*Rptr);
|
||||||
|
}
|
||||||
|
if(fabs(sum) < Z_prec) continue;
|
||||||
|
Zidxs_corr[k] = sum;
|
||||||
|
maxIdx = k;
|
||||||
|
}
|
||||||
|
gsl_matrix_free(R);
|
||||||
|
FREE(Zidxs);
|
||||||
|
FREE(Pn);
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
return Zidxs_corr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components of Zj gradient without constant factor
|
||||||
|
* @param n,m - orders of polynomial
|
||||||
|
* @param Sz - number of points
|
||||||
|
* @param P (i) - coordinates of reference points
|
||||||
|
* @param norm (o) - norm factor or NULL
|
||||||
|
* @return array of gradient's components
|
||||||
|
*/
|
||||||
|
point *gradZR(int n, int m, int Sz, polar *P, double *norm){
|
||||||
|
if(check_parameters(n, m, Sz, P)) return NULL;
|
||||||
|
point *gZ = NULL;
|
||||||
|
int j, k, m_abs = iabs(m), iup = (n-m_abs)/2, isum = (n+m_abs)/2;
|
||||||
|
double **Rpow = build_rpowR(n, Sz, P);
|
||||||
|
// now fill output matrix
|
||||||
|
gZ = MALLOC(point, Sz);
|
||||||
|
point *Zptr = gZ;
|
||||||
|
double ZSum = 0.;
|
||||||
|
polar *p = P;
|
||||||
|
for(j = 0; j < Sz; j++, p++, Zptr++){
|
||||||
|
if(p->r > 1.) continue; // throw out points with R>1
|
||||||
|
double theta = p->theta;
|
||||||
|
// components of grad Zj:
|
||||||
|
|
||||||
|
// 1. Theta_j; 2. dTheta_j/Dtheta
|
||||||
|
double Tj = 1., dTj = 0.;
|
||||||
|
if(m){
|
||||||
|
double costm, sintm;
|
||||||
|
sincos(theta*(double)m_abs, &sintm, &costm);
|
||||||
|
if(m > 0){
|
||||||
|
Tj = costm;
|
||||||
|
dTj = -m_abs * sintm;
|
||||||
|
}else{
|
||||||
|
Tj = sintm;
|
||||||
|
dTj = m_abs * costm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. R_j & dR_j/dr
|
||||||
|
double Rj = 0., dRj = 0.;
|
||||||
|
for(k = 0; k <= iup; k++){
|
||||||
|
double rj = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
|
||||||
|
/(//----------------------------------- ----- -------------------------------
|
||||||
|
FK[k]*FK[isum-k]*FK[iup-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
|
||||||
|
);
|
||||||
|
//DBG("rj = %g (n=%d, k=%d)\n",rj, n, k);
|
||||||
|
int kidx = n - 2*k;
|
||||||
|
Rj += rj * Rpow[kidx][j]; // *R^{n-2k}
|
||||||
|
if(kidx > 0)
|
||||||
|
dRj += rj * kidx * Rpow[kidx - 1][j]; // *(n-2k)*R^{n-2k-1}
|
||||||
|
}
|
||||||
|
// normalisation for Zernike
|
||||||
|
double eps_m = (m) ? 1. : 2., sq = sqrt(2.*(double)(n+1) / M_PI / eps_m);
|
||||||
|
Rj *= sq, dRj *= sq;
|
||||||
|
// 4. sin/cos
|
||||||
|
double sint, cost;
|
||||||
|
sincos(theta, &sint, &cost);
|
||||||
|
|
||||||
|
// projections of gradZj
|
||||||
|
double TdR = Tj * dRj, RdT = Rj * dTj / p->r;
|
||||||
|
Zptr->x = TdR * cost - RdT * sint;
|
||||||
|
Zptr->y = TdR * sint + RdT * cost;
|
||||||
|
// norm factor
|
||||||
|
ZSum += Zptr->x * Zptr->x + Zptr->y * Zptr->y;
|
||||||
|
}
|
||||||
|
if(norm) *norm = ZSum;
|
||||||
|
// free unneeded memory
|
||||||
|
free_rpow(&Rpow, n);
|
||||||
|
return gZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build components of vector polynomial Sj
|
||||||
|
* @param p - index of polynomial
|
||||||
|
* @param Sz - number of points
|
||||||
|
* @param P (i) - coordinates of reference points
|
||||||
|
* @param norm (o) - norm factor or NULL
|
||||||
|
* @return Sj(n,m) on image points
|
||||||
|
*/
|
||||||
|
point *zerngradR(int p, int Sz, polar *P, double *norm){
|
||||||
|
int n, m, i;
|
||||||
|
convert_Zidx(p, &n, &m);
|
||||||
|
if(n < 1) errx(1, "Order of gradient Z must be > 0!");
|
||||||
|
int m_abs = iabs(m);
|
||||||
|
double Zsum, K = 1.;
|
||||||
|
point *Sj = gradZR(n, m, Sz, P, &Zsum);
|
||||||
|
if(n != m_abs && n > 2){ // avoid trivial case: n = |m| (in case of n=2,m=0 n'=0 -> grad(0,0)=0
|
||||||
|
K = sqrt(((double)n+1.)/(n-1.));
|
||||||
|
Zsum = 0.;
|
||||||
|
point *Zj= gradZR(n-2, m, Sz, P, NULL);
|
||||||
|
point *Sptr = Sj, *Zptr = Zj;
|
||||||
|
for(i = 0; i < Sz; i++, Sptr++, Zptr++){
|
||||||
|
Sptr->x -= K * Zptr->x;
|
||||||
|
Sptr->y -= K * Zptr->y;
|
||||||
|
Zsum += Sptr->x * Sptr->x + Sptr->y * Sptr->y;
|
||||||
|
}
|
||||||
|
FREE(Zj);
|
||||||
|
}
|
||||||
|
if(norm) *norm = Zsum;
|
||||||
|
return Sj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decomposition of image with normals to wavefront by Zhao's polynomials
|
||||||
|
* by least squares method through LU-decomposition
|
||||||
|
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
|
||||||
|
* @param Sz, P(i) - size (Sz) of points array (P)
|
||||||
|
* @param grads(i) - wavefront gradients values in points P
|
||||||
|
* @param Zsz (o) - size of Z coefficients array
|
||||||
|
* @param lastIdx(o) - (if !NULL) last non-zero coefficient
|
||||||
|
* @return array of coefficients
|
||||||
|
*/
|
||||||
|
double *LS_gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx){
|
||||||
|
int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
if(Zsz) *Zsz = pmax;
|
||||||
|
// Now allocate matrix: its Nth row is equation for Nth data point,
|
||||||
|
// Mth column is Z_M
|
||||||
|
int Sz2 = Sz*2, x, y;
|
||||||
|
gsl_matrix *M = gsl_matrix_calloc(Sz2, pmax);
|
||||||
|
// fill matrix with coefficients
|
||||||
|
size_t T = M->tda, S = T * Sz; // T is matrix stride, S - index of second coefficient
|
||||||
|
for(x = 1; x < pmax; x++){
|
||||||
|
double norm;
|
||||||
|
int n, m;
|
||||||
|
convert_Zidx(x, &n, &m);
|
||||||
|
point *dZcoeff = gradZR(n, m, Sz, P, &norm), *dZptr = dZcoeff;
|
||||||
|
//point *dZcoeff = zerngradR(x, Sz, P, &norm), *dZptr = dZcoeff;
|
||||||
|
double *ptr = &(M->data[x]);
|
||||||
|
// X-component is top part of resulting matrix, Y is bottom part
|
||||||
|
for(y = 0; y < Sz; y++, ptr+=T, dZptr++){ // fill xth polynomial
|
||||||
|
*ptr = dZptr->x;
|
||||||
|
ptr[S] = dZptr->y;
|
||||||
|
}
|
||||||
|
FREE(dZcoeff);
|
||||||
|
}
|
||||||
|
|
||||||
|
gsl_vector *ans = gsl_vector_calloc(pmax);
|
||||||
|
gsl_vector *b = gsl_vector_calloc(Sz2);
|
||||||
|
double *ptr = b->data;
|
||||||
|
for(x = 0; x < Sz; x++, ptr++, grads++){
|
||||||
|
// fill components of vector b like components of matrix M
|
||||||
|
*ptr = grads->x;
|
||||||
|
ptr[Sz] = grads->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsl_vector *tau = gsl_vector_calloc(pmax);
|
||||||
|
gsl_linalg_QR_decomp(M, tau);
|
||||||
|
|
||||||
|
gsl_vector *residual = gsl_vector_calloc(Sz2);
|
||||||
|
gsl_linalg_QR_lssolve(M, tau, b, ans, residual);
|
||||||
|
ptr = &ans->data[1];
|
||||||
|
int maxIdx = 0;
|
||||||
|
double *Zidxs = MALLOC(double, pmax);
|
||||||
|
for(x = 1; x < pmax; x++, ptr++){
|
||||||
|
if(fabs(*ptr) < Z_prec) continue;
|
||||||
|
Zidxs[x] = *ptr;
|
||||||
|
maxIdx = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsl_matrix_free(M);
|
||||||
|
gsl_vector_free(ans);
|
||||||
|
gsl_vector_free(b);
|
||||||
|
gsl_vector_free(tau);
|
||||||
|
gsl_vector_free(residual);
|
||||||
|
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decomposition of image with normals to wavefront by Zhao's polynomials
|
||||||
|
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
|
||||||
|
* @param Sz, P(i) - size (Sz) of points array (P)
|
||||||
|
* @param grads(i) - wavefront gradients values in points P
|
||||||
|
* @param Zsz (o) - size of Z coefficients array
|
||||||
|
* @param lastIdx(o) - (if !NULL) last non-zero coefficient
|
||||||
|
* @return array of coefficients
|
||||||
|
*/
|
||||||
|
double *gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx){
|
||||||
|
int i, pmax, maxIdx = 0;
|
||||||
|
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
double *Zidxs = MALLOC(double, pmax);
|
||||||
|
point *icopy = MALLOC(point, Sz);
|
||||||
|
memcpy(icopy, grads, Sz*sizeof(point)); // make image copy to leave it unchanged
|
||||||
|
*Zsz = pmax;
|
||||||
|
for(i = 1; i < pmax; i++){ // now we fill array
|
||||||
|
double norm;
|
||||||
|
point *dZcoeff = zerngradR(i, Sz, P, &norm);
|
||||||
|
int j;
|
||||||
|
point *iptr = icopy, *zptr = dZcoeff;
|
||||||
|
double K = 0.;
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++)
|
||||||
|
K += zptr->x*iptr->x + zptr->y*iptr->y; // multiply matrixes to get coefficient
|
||||||
|
K /= norm;
|
||||||
|
if(fabs(K) < Z_prec)
|
||||||
|
continue; // there's no need to substract values that are less than our precision
|
||||||
|
Zidxs[i] = K;
|
||||||
|
maxIdx = i;
|
||||||
|
iptr = icopy; zptr = dZcoeff;
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++){
|
||||||
|
iptr->x -= K * zptr->x; // subtract composed coefficient to reduce high orders values
|
||||||
|
iptr->y -= K * zptr->y;
|
||||||
|
}
|
||||||
|
FREE(dZcoeff);
|
||||||
|
}
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
FREE(icopy);
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restoration of image with normals Zhao's polynomials coefficients
|
||||||
|
* all like Zcompose
|
||||||
|
* @return restored image
|
||||||
|
*/
|
||||||
|
point *gradZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P){
|
||||||
|
int i;
|
||||||
|
point *image = MALLOC(point, Sz);
|
||||||
|
for(i = 1; i < Zsz; i++){ // now we fill array
|
||||||
|
double K = Zidxs[i];
|
||||||
|
if(fabs(K) < Z_prec) continue;
|
||||||
|
point *Zcoeff = zerngradR(i, Sz, P, NULL);
|
||||||
|
int j;
|
||||||
|
point *iptr = image, *zptr = Zcoeff;
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++){
|
||||||
|
iptr->x += K * zptr->x;
|
||||||
|
iptr->y += K * zptr->y;
|
||||||
|
}
|
||||||
|
FREE(Zcoeff);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
386
Zernike/zernike_annular.c
Normal file
386
Zernike/zernike_annular.c
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
/*
|
||||||
|
* zernike_annular.c - annular Zernike polynomials
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These polynomials realized according to formulae in:
|
||||||
|
@ARTICLE{1981JOSA...71...75M,
|
||||||
|
author = {{Mahajan}, V.~N.},
|
||||||
|
title = "{Zernike annular polynomials for imaging systems with annular pupils.}",
|
||||||
|
journal = {Journal of the Optical Society of America (1917-1983)},
|
||||||
|
keywords = {Astronomical Optics},
|
||||||
|
year = 1981,
|
||||||
|
volume = 71,
|
||||||
|
pages = {75-85},
|
||||||
|
adsurl = {http://adsabs.harvard.edu/abs/1981JOSA...71...75M},
|
||||||
|
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ARTICLE{2006ApOpt..45.3442H,
|
||||||
|
author = {{Hou}, X. and {Wu}, F. and {Yang}, L. and {Wu}, S. and {Chen}, Q.
|
||||||
|
},
|
||||||
|
title = "{Full-aperture wavefront reconstruction from annular subaperture interferometric data by use of Zernike annular polynomials and a matrix method for testing large aspheric surfaces}",
|
||||||
|
journal = {\ao},
|
||||||
|
keywords = {Mathematics, Optical data processing, Metrology, Optical testing},
|
||||||
|
year = 2006,
|
||||||
|
month = may,
|
||||||
|
volume = 45,
|
||||||
|
pages = {3442-3455},
|
||||||
|
doi = {10.1364/AO.45.003442},
|
||||||
|
adsurl = {http://adsabs.harvard.edu/abs/2006ApOpt..45.3442H},
|
||||||
|
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "zernike.h"
|
||||||
|
#include "zern_private.h"
|
||||||
|
|
||||||
|
// epsilon (==rmin)
|
||||||
|
double eps = -1., eps2 = -1.; // this global variable changes with each conv_r call!
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert coordinates of points on ring into normalized coordinates in [0, 1]
|
||||||
|
* for further calculations of annular Zernike (expression for R_k^0)
|
||||||
|
* @param r0 (i) - original coordinates array
|
||||||
|
* @param Sz - size of r0
|
||||||
|
* @return dinamically allocated array with new coordinates, which zero element is
|
||||||
|
* (0,0)
|
||||||
|
*/
|
||||||
|
polar *conv_r(polar *r0, int Sz){
|
||||||
|
int x;
|
||||||
|
polar *Pn = MALLOC(polar, Sz);
|
||||||
|
double rmax = -1., rmin = 2.;
|
||||||
|
polar *p = r0, *p1 = Pn;
|
||||||
|
for(x = 0; x < Sz; x++, p++){
|
||||||
|
if(p->r < rmin) rmin = p->r;
|
||||||
|
if(p->r > rmax) rmax = p->r;
|
||||||
|
}
|
||||||
|
if(rmin > rmax || fabs(rmin - rmax) <= DBL_EPSILON || rmax > 1. || rmin < 0.)
|
||||||
|
errx(1, "polar coordinates should be in [0,1]!");
|
||||||
|
eps = rmin;
|
||||||
|
eps2 = eps*eps;
|
||||||
|
p = r0;
|
||||||
|
//rmax = 1. - eps2; // 1 - eps^2 -- denominator
|
||||||
|
rmax = rmax*rmax - eps2;
|
||||||
|
for(x = 0; x < Sz; x++, p++, p1++){
|
||||||
|
p1->theta = p->theta;
|
||||||
|
p1->r = sqrt((p->r*p->r - eps2) / rmax); // r = (r^2 - eps^2) / (1 - eps^2)
|
||||||
|
}
|
||||||
|
return Pn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate 2D array (H arrays of length W)
|
||||||
|
* @param len - array's width (number of elements in each 1D array
|
||||||
|
* @param num - array's height (number of 1D arrays)
|
||||||
|
* @return dinamically allocated 2D array filled with zeros
|
||||||
|
*/
|
||||||
|
_2D *_2ALLOC(int len, int num){
|
||||||
|
_2D *r = MALLOC(_2D, 1);
|
||||||
|
r->data = MALLOC(double*, num);
|
||||||
|
int x;
|
||||||
|
for(x = 0; x < num; x++)
|
||||||
|
r->data[x] = MALLOC(double, len);
|
||||||
|
r->len = (size_t)len;
|
||||||
|
r->num = (size_t)num;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _2FREE(_2D **a){
|
||||||
|
int x, W = (*a)->num;
|
||||||
|
for(x = 0; x < W; x++) free((*a)->data[x]);
|
||||||
|
free((*a)->data);
|
||||||
|
FREE(*a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zernike radial function R_n^0
|
||||||
|
* @param n - order of polynomial
|
||||||
|
* @param Sz - number of points
|
||||||
|
* @param P (i) - modified polar coordinates: sqrt((r^2-eps^2)/(1-eps^2))
|
||||||
|
* @param R (o) - array with size Sz to which polynomial will be stored
|
||||||
|
*/
|
||||||
|
void Rn0(int n, int Sz, polar *P, double *R){
|
||||||
|
if(Sz < 1 || !P)
|
||||||
|
errx(1, "Size of matrix must be > 0!");
|
||||||
|
if(n < 0)
|
||||||
|
errx(1, "Order of Zernike polynomial must be > 0!");
|
||||||
|
if(!FK) build_factorial();
|
||||||
|
int j, k, iup = n/2;
|
||||||
|
double **Rpow = build_rpowR(n, Sz, P);
|
||||||
|
// now fill output matrix
|
||||||
|
double *Zptr = R;
|
||||||
|
polar *p = P;
|
||||||
|
for(j = 0; j < Sz; j++, p++, Zptr++){
|
||||||
|
double Z = 0.;
|
||||||
|
if(p->r > 1.) continue; // throw out points with R>1
|
||||||
|
// calculate R_n^m
|
||||||
|
for(k = 0; k <= iup; k++){ // Sum
|
||||||
|
double F = FK[iup-k];
|
||||||
|
double z = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
|
||||||
|
/(//------------------------------------- ---------------------
|
||||||
|
FK[k]*F*F // k!((n/2-k)!)^2
|
||||||
|
);
|
||||||
|
Z += z * Rpow[n-2*k][j]; // *R^{n-2k}
|
||||||
|
}
|
||||||
|
*Zptr = Z;
|
||||||
|
//DBG("order %d, point (%g, %g): %g\n", n, p->r, p->theta, Z);
|
||||||
|
}
|
||||||
|
// free unneeded memory
|
||||||
|
free_rpow(&Rpow, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert parameters n, m into index in Noll notation, p
|
||||||
|
* @param n, m - Zernike orders
|
||||||
|
* @return order in Noll notation
|
||||||
|
*/
|
||||||
|
inline int p_from_nm(int n, int m){
|
||||||
|
return (n*n + 2*n + m)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create array [p][pt] of annular Zernike polynomials
|
||||||
|
* @param pmax - max number of polynomial in Noll notation
|
||||||
|
* @param Sz - size of points array
|
||||||
|
* @param P (i) - array with points' coordinates
|
||||||
|
* @param Norm(o)- array [0..pmax] of sum^2 for each polynomial (dynamically allocated) or NULL
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
#undef DBG
|
||||||
|
#define DBG(...)
|
||||||
|
_2D *ann_Z(int pmax, int Sz, polar *P, double **Norm){
|
||||||
|
int m, n, j, nmax, mmax, jmax, mW, jm;
|
||||||
|
convert_Zidx(pmax, &nmax, &mmax);
|
||||||
|
mmax = nmax; // for a case when p doesn't include a whole n powers
|
||||||
|
jmax = nmax / 2;
|
||||||
|
mW = mmax + 1; // width of row
|
||||||
|
jm = jmax + mW; // height of Q0 and h
|
||||||
|
polar *Pn = conv_r(P, Sz);
|
||||||
|
if(eps < 0. || eps2 < 0. || eps > 1. || eps2 > 1.)
|
||||||
|
errx(1, "Wrong epsilon value!!!");
|
||||||
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
_2D *h = _2ALLOC(mW, jm); // constants -> array [(jmax+1)+((mmax+1)-1)][mmax+1]
|
||||||
|
_2D *Q0= _2ALLOC(mW, jm); // Q(0) -> array [(jmax+1)+((mmax+1)-1)][mmax+1]
|
||||||
|
_2D *Q = _2ALLOC(Sz, (jmax + 1)*mW); // functions -> array [(jmax+1)*(mmax+1)][Sz]
|
||||||
|
DBG("Allocated Q&h, size of Q: %zdx%zd, size of H: %zdx%zd\n", Q->num, Q->len, h->num, h->len);
|
||||||
|
DBG("pmax = %d, nmax = mmax = %d, jmax = %d, jm = %d\n", pmax, nmax, jmax, jm);
|
||||||
|
DBG("eps=%g, eps2=%g\n", eps, eps2);
|
||||||
|
// in Q index of [j][m][point] is [j*mmax+m][point]
|
||||||
|
double **hd = h->data, **Qd = Q->data, **Q0d = Q0->data;
|
||||||
|
// fill h_j^0 & Q_j^0
|
||||||
|
for(j = 0; j < jm; j++){ // main matrix + triangle
|
||||||
|
DBG("j = %d, m = 0\n", j);
|
||||||
|
polar Zero = {0., 0.};
|
||||||
|
// h_j^0 = (1-eps^2)/(2(2j+1))
|
||||||
|
hd[j][0] = (1.-eps2)/(2.*j+1.)/2.;
|
||||||
|
// Q_j^0(rho, eps) = R_{2j}^0(r)
|
||||||
|
DBG("Q,h [%d][0], h=%g\n", j, hd[j][0]);
|
||||||
|
if(j <= jmax) Rn0(2*j, Sz, Pn, Qd[mW*j]);
|
||||||
|
Rn0(2*j, 1, &Zero, &Q0d[j][0]); // fill also Q(0)
|
||||||
|
}
|
||||||
|
FREE(Pn);
|
||||||
|
// fill h_j^m & Q_j&m
|
||||||
|
int JMAX = jm-1; // max number of j
|
||||||
|
for(m = 1; m <= mmax; m++, JMAX--){ // m in outern cycle because of Q_{j+1}!
|
||||||
|
DBG("\n\nm=%d\n",m);
|
||||||
|
int mminusone = m - 1;
|
||||||
|
int idx = mminusone;// index Q_j^{m-1}
|
||||||
|
for(j = 0; j < JMAX; j++, idx+=mW){
|
||||||
|
DBG("\nj=%d\n",j);
|
||||||
|
// 2(2j+2m-1) * h_j^{m-1}
|
||||||
|
// H_j^m = ------------------------------
|
||||||
|
// (j+m)(1-eps^2) * Q_j^{m-1}(0)
|
||||||
|
double H = 2*(2*(j+m)-1)/(j+m)/(1-eps2) * hd[j][mminusone]/Q0d[j][mminusone];
|
||||||
|
// h_j^m = -H_j^m * Q_{j+1}^{m-1}(0)
|
||||||
|
hd[j][m] = -H * Q0d[j+1][mminusone];
|
||||||
|
DBG("hd[%d][%d] = %g (H = %g)\n", j, m, hd[j][m], H);
|
||||||
|
// j Q_i^{m-1}(0) * Q_i^{m-1}(r)
|
||||||
|
// Q_j^m(r) = H_j^m * Sum -----------------------------
|
||||||
|
// i=0 h_i^{m-1}
|
||||||
|
if(j <= jmax){
|
||||||
|
int pt;
|
||||||
|
for(pt = 0; pt < Sz; pt++){ // cycle by points
|
||||||
|
int i, idx1 = mminusone;
|
||||||
|
double S = 0;
|
||||||
|
for(i = 0; i <=j; i++, idx1+=mW){ // cycle Sum
|
||||||
|
S += Q0d[i][mminusone] * Qd[idx1][pt] / hd[i][mminusone];
|
||||||
|
}
|
||||||
|
Qd[idx+1][pt] = H * S; // Q_j^m(rho^2)
|
||||||
|
//DBG("Q[%d][%d](%d) = %g\n", j, m, pt, H * S);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// and Q(0):
|
||||||
|
double S = 0.;
|
||||||
|
int i;
|
||||||
|
for(i = 0; i <=j; i++){
|
||||||
|
double qim = Q0d[i][mminusone];
|
||||||
|
S += qim*qim / hd[i][mminusone];
|
||||||
|
}
|
||||||
|
Q0d[j][m] = H * S; // Q_j^m(0)
|
||||||
|
DBG("Q[%d][%d](0) = %g\n", j, m, H * S);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_2FREE(&Q0);
|
||||||
|
|
||||||
|
// allocate memory for Z_p
|
||||||
|
_2D *Zarr = _2ALLOC(Sz, pmax + 1); // pmax is max number _including_
|
||||||
|
double **Zd = Zarr->data;
|
||||||
|
// now we should calculate R_n^m
|
||||||
|
DBG("R_{2j}^0\n");
|
||||||
|
// R_{2j}^0(rho) = Q_j^0(rho^2)
|
||||||
|
size_t S = Sz*sizeof(double);
|
||||||
|
for(j = 0; j <= jmax; j++){
|
||||||
|
int pidx = p_from_nm(2*j, 0);
|
||||||
|
if(pidx > pmax) break;
|
||||||
|
DBG("R[%d]\n", pidx);
|
||||||
|
memcpy(Zd[pidx], Qd[j*mW], S);
|
||||||
|
}
|
||||||
|
DBG("R_n^n\n");
|
||||||
|
// R_n^n(rho) = rho^n / sqrt(Sum_{i=0}^n[eps^{2i}])
|
||||||
|
double **Rpow = build_rpowR(nmax, Sz, P); // powers of rho
|
||||||
|
double epssum = 1., epspow = 1.;
|
||||||
|
for(n = 1; n <= nmax; n++){
|
||||||
|
int pidx = p_from_nm(n, -n); // R_n^{-n}
|
||||||
|
if(pidx > pmax) break;
|
||||||
|
DBG("R[%d] (%d, %d)\n", pidx, n, -n);
|
||||||
|
epspow *= eps2; // eps^{2n}
|
||||||
|
epssum += epspow;// sum_0^n eps^{2n}
|
||||||
|
double Sq = sqrt(epssum); // sqrt(sum...)
|
||||||
|
double *rptr = Zd[pidx], *pptr = Rpow[n];
|
||||||
|
int pt;
|
||||||
|
for(pt = 0; pt < Sz; pt++, rptr++, pptr++)
|
||||||
|
*rptr = *pptr / Sq; // rho^n / sqrt(...)
|
||||||
|
int pidx1 = p_from_nm(n, n); // R_n^{n}
|
||||||
|
if(pidx1 > pmax) break;
|
||||||
|
DBG("R[%d] (%d, %d)\n", pidx1, n, n);
|
||||||
|
memcpy(Zd[pidx1], Zd[pidx], S);
|
||||||
|
}
|
||||||
|
DBG("R_n^m\n");
|
||||||
|
/* / 1 - eps^2 \
|
||||||
|
R_{2j+|m|}^{|m|}(rho, eps) = sqrt| ------------------ | * rho^m * Q_j^m(rho^2)
|
||||||
|
\ 2(2j+|m|+1)*h_j^m / */
|
||||||
|
for(j = 1; j <= jmax; j++){
|
||||||
|
for(m = 1; m <= mmax; m++){
|
||||||
|
int N = 2*j + m, pt, pidx = p_from_nm(N, -m);
|
||||||
|
if(pidx > pmax) continue;
|
||||||
|
DBG("R[%d]\n", pidx);
|
||||||
|
double *rptr = Zd[pidx], *pptr = Rpow[m], *qptr = Qd[j*mW+m];
|
||||||
|
double hjm = hd[j][m];
|
||||||
|
for(pt = 0; pt < Sz; pt++, rptr++, pptr++, qptr++)
|
||||||
|
*rptr = sqrt((1-eps2)/2/(N+1)/hjm) * (*pptr) * (*qptr);
|
||||||
|
int pidx1 = p_from_nm(N, m);
|
||||||
|
if(pidx1 > pmax) continue;
|
||||||
|
DBG("R[%d]\n", pidx1);
|
||||||
|
memcpy(Zd[pidx1], Zd[pidx], S);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free_rpow(&Rpow, nmax);
|
||||||
|
_2FREE(&h);
|
||||||
|
_2FREE(&Q);
|
||||||
|
// last step: fill Z_n^m and find sum^2
|
||||||
|
int p;
|
||||||
|
double *sum2 = MALLOC(double, pmax+1);
|
||||||
|
for(p = 0; p <= pmax; p++){
|
||||||
|
convert_Zidx(p, &n, &m);
|
||||||
|
DBG("p=%d (n=%d, m=%d)\n", p, n, m);
|
||||||
|
int pt;
|
||||||
|
double (*fn)(double val);
|
||||||
|
double empty(double val){return 1.;}
|
||||||
|
if(m == 0) fn = empty;
|
||||||
|
else{if(m > 0)
|
||||||
|
fn = cos;
|
||||||
|
else
|
||||||
|
fn = sin;}
|
||||||
|
double m_abs = fabs((double)m), *rptr = Zd[p], *sptr = &sum2[p];
|
||||||
|
polar *Pptr = P;
|
||||||
|
for(pt = 0; pt < Sz; pt++, rptr++, Pptr++){
|
||||||
|
*rptr *= fn(m_abs * Pptr->theta);
|
||||||
|
//DBG("\tpt %d, val = %g\n", pt, *rptr);
|
||||||
|
*sptr += (*rptr) * (*rptr);
|
||||||
|
}
|
||||||
|
DBG("SUM p^2 = %g\n", *sptr);
|
||||||
|
}
|
||||||
|
if(Norm) *Norm = sum2;
|
||||||
|
else FREE(sum2);
|
||||||
|
return Zarr;
|
||||||
|
}
|
||||||
|
#undef DBG
|
||||||
|
#define DBG(...) do{fprintf(stderr, __VA_ARGS__); }while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose wavefront by koefficients of annular Zernike polynomials
|
||||||
|
* @params like for ZcomposeR
|
||||||
|
* @return composed wavefront (dynamically allocated)
|
||||||
|
*/
|
||||||
|
double *ann_Zcompose(int Zsz, double *Zidxs, int Sz, polar *P){
|
||||||
|
int i;
|
||||||
|
double *image = MALLOC(double, Sz);
|
||||||
|
_2D *Zannular = ann_Z(Zsz-1, Sz, P, NULL);
|
||||||
|
double **Zcoeff = Zannular->data;
|
||||||
|
for(i = 0; i < Zsz; i++){ // now we fill array
|
||||||
|
double K = Zidxs[i];
|
||||||
|
if(fabs(K) < Z_prec) continue;
|
||||||
|
int j;
|
||||||
|
double *iptr = image, *zptr = Zcoeff[i];
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++){
|
||||||
|
*iptr += K * (*zptr); // add next Zernike polynomial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_2FREE(&Zannular);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompose wavefront by annular Zernike
|
||||||
|
* @params like ZdecomposeR
|
||||||
|
* @return array of Zernike coefficients
|
||||||
|
*/
|
||||||
|
double *ann_Zdecompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
|
||||||
|
int i, pmax, maxIdx = 0;
|
||||||
|
double *Zidxs = NULL, *icopy = NULL;
|
||||||
|
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
|
||||||
|
Zidxs = MALLOC(double, pmax);
|
||||||
|
icopy = MALLOC(double, Sz);
|
||||||
|
memcpy(icopy, heights, Sz*sizeof(double)); // make image copy to leave it unchanged
|
||||||
|
*Zsz = pmax;
|
||||||
|
double *norm;
|
||||||
|
_2D *Zannular = ann_Z(pmax-1, Sz, P, &norm);
|
||||||
|
double **Zcoeff = Zannular->data;
|
||||||
|
for(i = 0; i < pmax; i++){ // now we fill array
|
||||||
|
int j;
|
||||||
|
double *iptr = icopy, *zptr = Zcoeff[i], K = 0., norm_i = norm[i];
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++)
|
||||||
|
K += (*zptr) * (*iptr) / norm_i; // multiply matrixes to get coefficient
|
||||||
|
if(fabs(K) < Z_prec)
|
||||||
|
continue; // there's no need to substract values that are less than our precision
|
||||||
|
Zidxs[i] = K;
|
||||||
|
maxIdx = i;
|
||||||
|
iptr = icopy; zptr = Zcoeff[i];
|
||||||
|
for(j = 0; j < Sz; j++, iptr++, zptr++)
|
||||||
|
*iptr -= K * (*zptr); // subtract composed coefficient to reduce high orders values
|
||||||
|
}
|
||||||
|
if(lastIdx) *lastIdx = maxIdx;
|
||||||
|
_2FREE(&Zannular);
|
||||||
|
FREE(norm);
|
||||||
|
FREE(icopy);
|
||||||
|
return Zidxs;
|
||||||
|
}
|
||||||
139
bidirectional_list.c
Normal file
139
bidirectional_list.c
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* bidirectional_list.c - bidi sorted list
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#include "bidirectional_list.h"
|
||||||
|
|
||||||
|
#ifndef Malloc
|
||||||
|
#define Malloc my_alloc
|
||||||
|
/**
|
||||||
|
* Memory allocation with control
|
||||||
|
* @param N - number of elements
|
||||||
|
* @param S - size of one element
|
||||||
|
* @return allocated memory or die
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef FREE
|
||||||
|
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
List *l_search(List *root, int v, List **Left, List **Right){
|
||||||
|
List *L = NULL, *R = NULL;
|
||||||
|
int dir = -1;
|
||||||
|
if(!root) goto not_Fnd;
|
||||||
|
if(root->data == v)
|
||||||
|
return root;
|
||||||
|
if(root->data < v) dir = 1;
|
||||||
|
do{
|
||||||
|
L = root->left_p; // left leaf
|
||||||
|
R = root->right_p; // right leaf
|
||||||
|
int D = root->data;// data in leaf
|
||||||
|
if(D > v && dir == -1){ // search to left is true
|
||||||
|
R = root;
|
||||||
|
root = L;
|
||||||
|
}
|
||||||
|
else if(D < v && dir == 1){ // search to right is true
|
||||||
|
L = root;
|
||||||
|
root = R;
|
||||||
|
}
|
||||||
|
else if(D == v){ // we found exact value
|
||||||
|
if(Left) *Left = L;
|
||||||
|
if(Right) *Right = R;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}while(root); // if root == NULL, we catch an edge
|
||||||
|
// exact value not found
|
||||||
|
if(root){ // we aren't on an edge
|
||||||
|
if(dir == -1) L = root;
|
||||||
|
else R = root;
|
||||||
|
}
|
||||||
|
not_Fnd:
|
||||||
|
if(Left) *Left = L;
|
||||||
|
if(Right) *Right = R;
|
||||||
|
return NULL; // value not found
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List *l_insert(List *root, int v){
|
||||||
|
List *node, *L, *R;
|
||||||
|
node = l_search(root, v, &L, &R);
|
||||||
|
if(node) return node; // we found exact value V
|
||||||
|
// there's no exact value: insert new
|
||||||
|
if((node = (List*) Malloc(1, sizeof(List))) == 0) return NULL; // allocation error
|
||||||
|
node->data = v; // insert data
|
||||||
|
// add neighbours:
|
||||||
|
node->left_p = L;
|
||||||
|
node->right_p = R;
|
||||||
|
// fix neighbours:
|
||||||
|
if(L) L->right_p = node;
|
||||||
|
if(R) R->left_p = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void l_remove(List **root, int v){
|
||||||
|
List *node, *left, *right;
|
||||||
|
if(!root) return;
|
||||||
|
if(!*root) return;
|
||||||
|
node = l_search(*root, v, &left, &right);
|
||||||
|
if(!node) return; // there's no such element
|
||||||
|
if(node == *root) *root = node->right_p;
|
||||||
|
if(!*root) *root = node->left_p;
|
||||||
|
FREE(node);
|
||||||
|
if(left) left->right_p = right;
|
||||||
|
if(right) right->left_p = left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
int main(void) {
|
||||||
|
List *tp, *root_p = NULL;
|
||||||
|
int i, ins[] = {4,2,6,1,3,4,7};
|
||||||
|
for(i = 0; i < 7; i++)
|
||||||
|
if(!(root_p = l_insert(root_p, ins[i])))
|
||||||
|
err(1, "Malloc error"); // can't insert
|
||||||
|
for(i = 0; i < 9; i++){
|
||||||
|
tp = l_search(root_p, i, NULL, NULL);
|
||||||
|
printf("%d ", i);
|
||||||
|
if(!tp) printf("not ");
|
||||||
|
printf("found\n");
|
||||||
|
}
|
||||||
|
printf("now delete items 1, 4 and 8\n");
|
||||||
|
l_remove(&root_p, 1);
|
||||||
|
l_remove(&root_p, 4);
|
||||||
|
l_remove(&root_p, 8);
|
||||||
|
for(i = 0; i < 9; i++){
|
||||||
|
tp = l_search(root_p, i, NULL, NULL);
|
||||||
|
printf("%d ", i);
|
||||||
|
if(!tp) printf("not ");
|
||||||
|
printf("found\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
34
bidirectional_list.h
Normal file
34
bidirectional_list.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* bidirectional_list.h
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __BIDIRECTIONAL_LIST_H__
|
||||||
|
#define __BIDIRECTIONAL_LIST_H__
|
||||||
|
|
||||||
|
typedef struct list_node{
|
||||||
|
int data;
|
||||||
|
struct list_node *left_p, *right_p;
|
||||||
|
} List;
|
||||||
|
|
||||||
|
List *l_search(List *root, int v, List **Left, List **Right);
|
||||||
|
List *l_insert(List *root, int v);
|
||||||
|
void l_remove(List **root, int v);
|
||||||
|
|
||||||
|
#endif // __BIDIRECTIONAL_LIST_H__
|
||||||
111
cmakelists_/CMakeLists_regular_01.txt
Normal file
111
cmakelists_/CMakeLists_regular_01.txt
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
set(PROJ tvguide)
|
||||||
|
set(MINOR_VERSION "0")
|
||||||
|
set(MID_VERSION "0")
|
||||||
|
set(MAJOR_VERSION "0")
|
||||||
|
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||||
|
|
||||||
|
message("VER: ${VERSION}")
|
||||||
|
|
||||||
|
# default flags
|
||||||
|
set(CFLAGS -O2 -Wextra -Wall -Werror -W -std=gnu99)
|
||||||
|
|
||||||
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
|
|
||||||
|
# here is one of two variants: all .c in directory or .c files in list
|
||||||
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||||
|
#set(SOURCES list_of_c_files)
|
||||||
|
|
||||||
|
# we can change file list
|
||||||
|
#if(NOT DEFINED something)
|
||||||
|
# set(SOURCES ${SOURCES} one_more_list)
|
||||||
|
# add_definitions(-DSOME_DEFS)
|
||||||
|
#endif()
|
||||||
|
|
||||||
|
# cmake -DDEBUG=1 -> debugging
|
||||||
|
if(DEFINED DEBUG)
|
||||||
|
add_definitions(-DEBUG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# directory should contain dir locale/ru for gettext translations
|
||||||
|
set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru)
|
||||||
|
|
||||||
|
if(NOT DEFINED LOCALEDIR)
|
||||||
|
if(DEFINED DEBUG)
|
||||||
|
set(LOCALEDIR ${CMAKE_CURRENT_SOURCE_DIR}/locale)
|
||||||
|
else()
|
||||||
|
set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# gettext modules
|
||||||
|
set(MODULES libavformat libavcodec libswscale libavutil libavdevice)
|
||||||
|
# additional modules on condition
|
||||||
|
#if(DEFINED SOMETHING)
|
||||||
|
# set(MODULES ${MODULES} more_modules>=version)
|
||||||
|
# add_definitions(-DSOMEDEFS)
|
||||||
|
#endif()
|
||||||
|
|
||||||
|
project(${PROJ})
|
||||||
|
# change wrong behaviour with install prefix
|
||||||
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
||||||
|
message("Change default install path to /usr")
|
||||||
|
set(CMAKE_INSTALL_PREFIX "/usr")
|
||||||
|
endif()
|
||||||
|
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
# gettext files
|
||||||
|
set(PO_FILE ${LCPATH}/messages.po)
|
||||||
|
set(MO_FILE ${LCPATH}/LC_MESSAGES/${PROJ}.mo)
|
||||||
|
set(RU_FILE ${LCPATH}/ru.po)
|
||||||
|
|
||||||
|
# pkgconfig
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
|
||||||
|
|
||||||
|
# exe file
|
||||||
|
add_executable(${PROJ} ${SOURCES} ${PO_FILE} ${MO_FILE})
|
||||||
|
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES})
|
||||||
|
include_directories(${${PROJ}_INCLUDE_DIRS})
|
||||||
|
link_directories(${${PROJ}_LIBRARY_DIRS})
|
||||||
|
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||||
|
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
|
||||||
|
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
||||||
|
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||||
|
|
||||||
|
# Installation of the program
|
||||||
|
INSTALL(FILES ${MO_FILE} DESTINATION "share/locale/ru/LC_MESSAGES")
|
||||||
|
#PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
|
||||||
|
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
||||||
|
#PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
# Script to be executed at installation time (kind of post-intallation script) to
|
||||||
|
# change the right accesses on the installed files
|
||||||
|
#INSTALL(SCRIPT inst.cmake)
|
||||||
|
|
||||||
|
find_package(Gettext REQUIRED)
|
||||||
|
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
|
||||||
|
if(NOT GETTEXT_XGETTEXT_EXECUTABLE OR NOT GETTEXT_MSGFMT_EXECUTABLE)
|
||||||
|
message(FATAL_ERROR "xgettext not found")
|
||||||
|
endif()
|
||||||
|
file(MAKE_DIRECTORY ${LCPATH})
|
||||||
|
file(MAKE_DIRECTORY ${LCPATH}/LC_MESSAGES)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${PO_FILE}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE}
|
||||||
|
COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE}
|
||||||
|
COMMAND enconv ${PO_FILE}
|
||||||
|
DEPENDS ${SOURCES}
|
||||||
|
)
|
||||||
|
# we need this to prewent ru.po from deleting by make clean
|
||||||
|
add_custom_target(
|
||||||
|
RU_FILE
|
||||||
|
COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE}
|
||||||
|
DEPENDS ${PO_FILE}
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${MO_FILE}
|
||||||
|
COMMAND make RU_FILE && ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE}
|
||||||
|
DEPENDS ${PO_FILE}
|
||||||
|
)
|
||||||
19
erosion-dilation/Makefile
Normal file
19
erosion-dilation/Makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
PROGRAM = binmorph_test
|
||||||
|
CXX.SRCS = binmorph.c main.c
|
||||||
|
CC = gcc
|
||||||
|
LDFLAGS = -lm -fopenmp
|
||||||
|
CXX = gcc
|
||||||
|
DEFINES = -DEBUG -DOMP
|
||||||
|
CFLAGS = -fopenmp -Wall -Werror -O3 -std=gnu99 $(DEFINES)
|
||||||
|
OBJS = $(CXX.SRCS:.c=.o)
|
||||||
|
all : $(PROGRAM) clean
|
||||||
|
binmorph.c : cclabling.h cclabling_1.h dilation.h fs_filter.h fc_filter.h
|
||||||
|
$(PROGRAM) : $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM)
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(CXX.SRCS)
|
||||||
|
|
||||||
|
### <DEPENDENCIES ON .h FILES GO HERE>
|
||||||
|
# name1.o : header1.h header2.h ...
|
||||||
523
erosion-dilation/README
Normal file
523
erosion-dilation/README
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
************************************
|
||||||
|
СООБРАЖЕНИЯ ПО ПОВОДУ ЭРОЗИИ/ДИЛЯЦИИ
|
||||||
|
************************************
|
||||||
|
|
||||||
|
|
||||||
|
Эрозия
|
||||||
|
|
||||||
|
00000 00000
|
||||||
|
01110 00000
|
||||||
|
01110 => 00100
|
||||||
|
01110 00000
|
||||||
|
00000 00000
|
||||||
|
|
||||||
|
Бит равен единице лишь тогда, когда присутствуют все его 4 связи.
|
||||||
|
Т.е. для срединных битов байта выполняем & текущего байта с байтами верхней и нижней строк. Для крайних битов необходимо проверить еще и 4-связные байты:
|
||||||
|
|
||||||
|
ABC
|
||||||
|
DEF
|
||||||
|
GHK
|
||||||
|
|
||||||
|
Младший бит E проверяется по старшему биту F, младшим битам B и H и второму биту E.
|
||||||
|
Старший бит E проверяется по младшему биту D, старшим битам B и E и седьмому биту E.
|
||||||
|
|
||||||
|
Таким образом, алгоритм получается следующим:
|
||||||
|
1. подменяем E маской[E] (эрозия внутренних битов)
|
||||||
|
2. делаем &: E = E & B & H
|
||||||
|
3. корректируем старший бит E по младшему D и младший бит E по старшему F.
|
||||||
|
|
||||||
|
ПРОВЕРИТЬ, ЧТО БЫСТРЕЙ: (A&0X80)>>8 ИЛИ РАЗЫМЕНОВАНИЕ УКАЗАТЕЛЯ (ПО ТАБЛИЦЕ)
|
||||||
|
|
||||||
|
|
||||||
|
Диляция
|
||||||
|
|
||||||
|
00000 00110
|
||||||
|
00110 01111
|
||||||
|
01010 => 11111
|
||||||
|
01100 11110
|
||||||
|
00000 01100
|
||||||
|
|
||||||
|
Обратная ситуация: вместо & делаем | :
|
||||||
|
1. подменяем E маской (диляция внутренних битов)
|
||||||
|
2. делаем |: E = E | B | H
|
||||||
|
3. корректируем старший и младший биты операцией |.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**************************************************
|
||||||
|
СООБРАЖЕНИЯ ПО ПОВОДУ ВЫДЕЛЕНИЯ СВЯЗАННЫХ ОБЛАСТЕЙ
|
||||||
|
**************************************************
|
||||||
|
|
||||||
|
Вернемся к тому же общему случаю: искомый пиксель нахдится в байте E.
|
||||||
|
ABC
|
||||||
|
DEF
|
||||||
|
GHK
|
||||||
|
|
||||||
|
Фильтрация 4-связанных областей схожа с операцией диляции за исключением собственно
|
||||||
|
логики - в отсечении пикселей, не являющихся 4-связанными, необходимо провести операцию:
|
||||||
|
|
||||||
|
E4 = E & ( B|H|(E<<1)|(E>>1)|(D<<7)|(F>>7) )
|
||||||
|
|
||||||
|
Т.е. мы оставляем пиксель в E лишь в том случае, если хотя бы один из его
|
||||||
|
4-связанных соседей присутствует.
|
||||||
|
|
||||||
|
Соответственно, для крайних пикселей (крайние столбцы/строки) выражение для E4
|
||||||
|
будет видоизменяться (для крайних строк - наличием/отсутствием B,H; для крайних
|
||||||
|
столбцов - наличием/отсутствием выражений с D, F).
|
||||||
|
|
||||||
|
Для упрощения записи можно было бы сделать макрос при помощи boost, но что-то
|
||||||
|
лень мне в дебри буста погружаться, поэтому проблему решаю простым копипастом.
|
||||||
|
|
||||||
|
Кстати, здесь нет проверки A,C,G и K, иначе пришлось бы еще больше кода наворачивать
|
||||||
|
(добавились бы особые случаи для углов изображения).
|
||||||
|
|
||||||
|
|
||||||
|
Общее выражение для поиска 8-связанных областей несколько видоизменится:
|
||||||
|
|
||||||
|
пусть {x} = (x|(x<<1)|(x>>1)), тогда
|
||||||
|
E8 = E & ( {B|H} | ((E<<1)|(E>>1)) | ((C|F|K)>>7) | ((A|D|G)<<7) )
|
||||||
|
|
||||||
|
Вычислительная сложность сильно возрастает, причем многие операции вычисляются по
|
||||||
|
два раза. Пожалуй, можно попытаться немного ускорить вычисления, если заранее
|
||||||
|
подготовить матрицу {x} (и матрицы (x<<7) и (x>>7) ???).
|
||||||
|
/* (сомнительно) Аналогично, заранее подготовив матрицы x<<7) и (x>>7) можно
|
||||||
|
* попытаться ускорить поиск E4 */
|
||||||
|
|
||||||
|
|
||||||
|
Когда мы уже имеем отфильтрованное изображение, нужно "всего лишь" пробежаться
|
||||||
|
по нему в поисках областей, со всех сторон ограниченных нулями. Грубо говоря,
|
||||||
|
мы маркируем 8-связанные области без проверки на 8-связанность.
|
||||||
|
|
||||||
|
Первое, что приходит на ум - двухпроходная маркировка: сначала мы пробегаемся
|
||||||
|
по связанной области, постепенно расширяя границы заключающего ее прямоугольника.
|
||||||
|
Затем мы выделяем память для этого кусочка и заполняем его, пробегаясь по нашей
|
||||||
|
области еще раз (ну или делаем полную копию содержимого бокса, а затем выкидываем
|
||||||
|
все, что не принадлежит искомой связанной области).
|
||||||
|
|
||||||
|
Еще вариант - сразу выделять область памяти (вспомогательную) размером со ВСЕ
|
||||||
|
первоначальное изображение (гениально!). В этом случае мы сможем сразу же копировать
|
||||||
|
в эту область памяти нашу х-связанную область, а затем, когда она целиком будет
|
||||||
|
скопирована и будут ясны параметры описывающего ее прямоугольника, выделяем уже
|
||||||
|
память именно под эту область и копируем в нее содержимое бокса.
|
||||||
|
|
||||||
|
Логично было бы преобразовать картинку до анализа в бинарный вид, поэтому сначала
|
||||||
|
будем предполагать, что мы имеем дело с бинарным изображением (а не "сжатым"
|
||||||
|
битовым).
|
||||||
|
|
||||||
|
Теперь каждый пиксель нашей матрицы соответствует одному пикселю изображения
|
||||||
|
ABC
|
||||||
|
DEF
|
||||||
|
GHK
|
||||||
|
|
||||||
|
На ум приходит использовать метод шагающих квадратов, однако, он применяется
|
||||||
|
для обхода границ -> мало чем поможет (разве что первоначальным определением
|
||||||
|
размеров бокса, но я уже решил делать лишь один проход обнаружения связанных
|
||||||
|
областей). Интернета под рукой нет, чтобы подсмотреть наиболее эффективный
|
||||||
|
способ, поэтому буду изобретать велосипед.
|
||||||
|
|
||||||
|
|
||||||
|
=== Нулевое приближение ===
|
||||||
|
|
||||||
|
/*
|
||||||
|
Работаем с копией изображения, т.к. уже пройденные значения будем обнулять.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Итак, выделим память под промежуточный буфер (сразу обнулив ее) и начнем
|
||||||
|
проходить изображение с левого верхнего угла. Как только мы натыкаемся
|
||||||
|
на единицу в E, записываем в соответствующие координаты буфера 1.
|
||||||
|
Далее смотрим, куда нам нужно двигаться. Считаем, что в A,B,C и D нули (т.к. там -
|
||||||
|
уже пройденная область). Нам необходимо проверить пиксели F, G, H и K.
|
||||||
|
Теперь каждый из них становится E. Если кто-то из них установлен в 1, проверяем
|
||||||
|
его соседей и т.д. Однако, на всех шагах, отличных от первого, мы уже обязаны
|
||||||
|
проверить ВСЕХ СОСЕДЕЙ, т.к. не факт, что наша область не представляет собою
|
||||||
|
какой-нибудь извращенной фигуры с отростками и многочисленными полостями.
|
||||||
|
|
||||||
|
На ум приходит рекурсия, однако, учитывая то, что изображения могут быть довольно-таки
|
||||||
|
огромными, с горем расстаюсь с этой идеей. А так было бы хорошо: рекурсия сделала
|
||||||
|
бы очень простым этот алгоритм. Но регистры не бесконечные.
|
||||||
|
|
||||||
|
Кстати, если не преобразовывать изображение в бинарное, то вполне возможны случаи,
|
||||||
|
когда один и тот же байт будет проходиться вплоть до четырех раз!
|
||||||
|
|
||||||
|
После копирования каждого пикселя с 1 в результирующий буфер, "стираем" его
|
||||||
|
с оригинала (сбросом в 0). По окончанию операций обнуляем буфер.
|
||||||
|
|
||||||
|
Тут мне опять пришла в голову идея о шагающих квадратах: а что, если в первый
|
||||||
|
проход пробежаться по границе области, определить описывающий ее прямоугольник,
|
||||||
|
выделить буфер по размеру прямоугольника (это - как раз искомое возвращаемое
|
||||||
|
функцией значение); после этого, проходя по объекту, пиксель за пикселем копировать
|
||||||
|
его в буфер, "стирая" с оригинала. Правда, здесь опять та же самая проблема:
|
||||||
|
нет гарантии, что за один проход все будет сделано.
|
||||||
|
|
||||||
|
|
||||||
|
=== "Гюйгенс". ===
|
||||||
|
Один из вариантов прохождения по изображению в поисках связанных областей (который,
|
||||||
|
собственно, первым пришел мне в голову еще вчера, но лучше всего, конечно, он бы
|
||||||
|
выглядел в рекурсивном исполнении).
|
||||||
|
|
||||||
|
== лирика по поводу рекурсии ==
|
||||||
|
Еще раз, чтобы убедиться лишний раз в том, что рекурсия невозможна, я набросал
|
||||||
|
элементарнейшую проверялку:
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int incr(int a){
|
||||||
|
fprintf(stderr, "%d ", a++);
|
||||||
|
incr(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
main(){
|
||||||
|
incr(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Проверялочка эта должна вылетать с сегфолтом как только скончаются все ресурсы
|
||||||
|
на рекурсию. Получилось вот что:
|
||||||
|
|
||||||
|
... 261504 Ошибка сегментирования (core dumped)
|
||||||
|
... 261350 Ошибка сегментирования (core dumped)
|
||||||
|
... 261333 Ошибка сегментирования (core dumped)
|
||||||
|
|
||||||
|
ЕМНИП, что-то подобное было на ЛОРе. И, возможно, предел этот даже как-то регулируется.
|
||||||
|
Число это подозрительно близко к 256к (262144): возможно, оно так и есть (просто
|
||||||
|
еще кучу места в стеке занимают всякие вспомогательные буферы из stdio).
|
||||||
|
|
||||||
|
Я немного видоизменил программку: она теперь еще и по 1к выделяет:
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int incr(int a){
|
||||||
|
char *x = malloc(1024);
|
||||||
|
memset(x, 1, 1024);
|
||||||
|
fprintf(stderr, "%d ", a++);
|
||||||
|
incr(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
main(){
|
||||||
|
incr(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Теперь цифры "более другие":
|
||||||
|
|
||||||
|
... 174258 Ошибка сегментирования (core dumped)
|
||||||
|
... 174372 Ошибка сегментирования (core dumped)
|
||||||
|
... 174325 Ошибка сегментирования (core dumped)
|
||||||
|
|
||||||
|
Кто сожрал 85к? Почему именно 85к? Заменяю выделение памяти на `char x = 2;` -
|
||||||
|
получаю то же самое. Добавление переменных ничего не меняет.
|
||||||
|
В любом случае, все-таки про рекурсию придется забыть: если искомая область будет
|
||||||
|
иметь площадь, превышающую эдак 170 тысяч пикселей (а это не так уж и много), то
|
||||||
|
я получу большой фигвам.
|
||||||
|
## конец лирики
|
||||||
|
|
||||||
|
Принцип Гюйгенса прост: каждая точка волнового фронта (в нашем случае - точка
|
||||||
|
внутри искомой области) является источником нового волнового фронта (в нашем
|
||||||
|
случае - центром, от которого "расходится" поисковая волна).
|
||||||
|
|
||||||
|
Пробегаясь такой "волной" от первого обнаруженного пикселя, принадлежащего
|
||||||
|
искомой фигуре, мы пройдем все ее закоулки.
|
||||||
|
Однако, проблема в том, как реализовать это без бешеного количества разыменования
|
||||||
|
указателей и проверок.
|
||||||
|
|
||||||
|
Долго кумекая, я так и не придумал, как реализовать "Гюйгенса", не вводя уйму
|
||||||
|
промежуточных матриц.
|
||||||
|
|
||||||
|
|
||||||
|
### "Гюйгенс модифицированный" ###
|
||||||
|
Для ограничения одной-единственной вспомогательной матрицей, можно попробовать
|
||||||
|
оставить одну "волну": от первоначальной точки проводим расходящуюся во все
|
||||||
|
стороны "волну". Пусть для простоты она будет "квадратной".
|
||||||
|
Пробегаясь по всем точкам очередного фронта "волны", кроме особых (о них -
|
||||||
|
позже, их тоже надо контролировать), "стираем" единицы с изображения, перенося их в маску.
|
||||||
|
Как только натыкаемся на нуль, если до этого тоже был нуль, заносим единичку в
|
||||||
|
матрицу спец.маски в соответствующий пиксель, а также в пиксель,
|
||||||
|
соседний с текущим, но расположеный на "волне" с радиусом на 1 больше (т.е. для левой
|
||||||
|
поверхности переносим пиксель влево на 1, для правой - вправо на 1 и т.п.).
|
||||||
|
Если натыкаемся на 1, делаем обнуление соответствующего пикселя на "волне" с радиусом
|
||||||
|
на 1 больше + его предыдущего соседа в спец.маске. Таким образом, "тени" получаются постепенно
|
||||||
|
сходящимися. Но все равно, остается возможность того, что эти "тени" что-нибудь да
|
||||||
|
сокроют, не говоря уже о том, что эта "волна" только в очень редких случаях сразу
|
||||||
|
выявит хотя бы внешний контур искомой фигуры.
|
||||||
|
Именно для этого и нужна наша спец.маска: как только "кончится" искомая фигура по
|
||||||
|
"большой волне", нужно будет просмотреть все пиксели маски (внутри уже получившегося
|
||||||
|
прямоугольника + 1 по радиусу, т.к. снаружи все равно нули), пробегая по их
|
||||||
|
внешней границе: как только обнаруживаем 1 в изображении, "выпускаем" новую "волну".
|
||||||
|
Пока бежим первый раз, пропускаем все единички в маске, копируя их на "волну" маски
|
||||||
|
с большим радиусом.
|
||||||
|
|
||||||
|
Метод получился опять каким-то уж очень извращенным. Глаза уже слипаются, поэтому
|
||||||
|
пока - отбой.
|
||||||
|
|
||||||
|
=== первое приближение к решению ===
|
||||||
|
Итак, сегодня я накачал всяких статеек. Почитал. Обычный алгоритм выделения
|
||||||
|
связанных областей (как я мог о нем не вспомнить?) жутко тормозной: он требует
|
||||||
|
как минимум четыре прохода (1 - маркировка соседних областей, 2 - построение
|
||||||
|
списка эквивалентности номеров, 3 - переименование областей, 4 - выделение
|
||||||
|
объектов). При этом на первом шаге мы должны проверять, были ли до пикселя нули,
|
||||||
|
на втором - верхнего соседа (и его боковых, если нужно заполнить 8-связанную
|
||||||
|
область), на третьем - сверяться с таблицей, на четвертом -
|
||||||
|
многократно сканировать изображение, пока все объекты не будут выделены.
|
||||||
|
Конечно, четвертый шаг можно укоротить: на третьем же шаге выделить массив
|
||||||
|
прямоугольников и обновлять размеры соответствующего прямоугольника.
|
||||||
|
Тогда четвертый шаг сведется к сканированию внутренностей каждого прямоугольника
|
||||||
|
с заполнением его маски единицами там, где в массиве номеров пиксель == N.
|
||||||
|
|
||||||
|
Процедура перемаркировки такова:
|
||||||
|
проходя матрицу с маркерами, проверяем каждый пиксель. Если пиксель нулевой,
|
||||||
|
устанавливаем флаг обнаружения в false и продолжаем. Если флаг == true, это значит,
|
||||||
|
что данный номер мы уже перемаркировали - продолжаем.
|
||||||
|
Если же пиксель - "свеженайденный", надо проверить все 6 пикселей (для 8-связных
|
||||||
|
областей) в предыдущем и последующем рядах (снизу - т.к. возможен вариант коротенького
|
||||||
|
кусочка, а то и единичного пикселя над длинной линией - из-за флага обнаружения мы
|
||||||
|
пролетим под ним, а вот на этапе сканирования его, изучая то, что под ним, сделаем
|
||||||
|
правильную ремаркировку). Вполне возможно, что нет необходимости проверять сразу и
|
||||||
|
пиксель над, и под нашим: пиксель точно под ним по-любому будет проверен (он - либо
|
||||||
|
часть более длинной линии, которую мы обнаружим в DL или DR углах, либо часть
|
||||||
|
вертикальной, которая обнаружится при сканировании нижней линии).
|
||||||
|
|
||||||
|
А вообще, полезно рассмотреть, какие варианты расположения соседей
|
||||||
|
могут быть, чтобы не пропустить мелочей (нумерация не важна, поэтому просто 1/0;
|
||||||
|
кроме того, т.к вариантов аж 256, рассмотрим лишь некоторые):
|
||||||
|
(X - что угодно, U - единица в одном из пикселей, V - единица в одном и более пикселей)
|
||||||
|
|
||||||
|
A B C D E F G H I J K L M N O P Q R
|
||||||
|
XXX 000 UUU 000 101 101 11X 000 010 000 000 000 000 000 000 000 000 000
|
||||||
|
11X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X 01X
|
||||||
|
XXX 000 000 VVV 000 VVV VVV 010 000 000 000 000 000 000 000 000 000 000
|
||||||
|
|
||||||
|
A - просто проходим мимо
|
||||||
|
B - инкрементируем счетчик уникальных объектов и перемаркируем текущий номер
|
||||||
|
C - ремаркируем текущее значение на то, которое содержится в массиве с индексом,
|
||||||
|
имеющим значение счетчика в пикселе с единичкой
|
||||||
|
D - то же, что в B + ремаркировка значений в нижней строке
|
||||||
|
E - ремаркировка текущего + правого верхнего счетчика по массиву с индексом,
|
||||||
|
равным значению счетчика в левом верхнем пикселе
|
||||||
|
F - ремаркировка всех по левому верхнему
|
||||||
|
G - для упрощения можно сделать эквивалентом F: нет нужды усложнять алгоритм ради
|
||||||
|
пары лишних операций
|
||||||
|
H - нет нужды проверять этот вариант, т.к. на стадии I он ремаркируется
|
||||||
|
I - заменяет H
|
||||||
|
|
||||||
|
Итак, общие тенденции:
|
||||||
|
- Если в верхней строке ничего нет, проверяем, не является ли наше текущее
|
||||||
|
значение счетчика уже перемаркированным: если является, оставляем как есть,
|
||||||
|
иначе - счетчик уникальных объектов инкрементируется и текущее значение перемаркируется
|
||||||
|
им; объекты внизу (если есть) тоже перемаркируются им
|
||||||
|
- если в левом верхнем углу !=0, все номера перемаркируются им, а пиксель над
|
||||||
|
нами проверять нет нужды
|
||||||
|
- если обнаружен !=0 ровно над нами, правый верхний проверять нет нужды, а оставшиеся
|
||||||
|
перемаркируются им
|
||||||
|
|
||||||
|
При организации процедуры переименования я натолкнулся на следующее: если ряд
|
||||||
|
X переименовывается в нижележащий Y, а потом Y переименовывается в Z, то ряд
|
||||||
|
X так и остается переименованным в Y.
|
||||||
|
Следовательно, при переименовании в массиве ассоциаций на стадии перенумерования
|
||||||
|
надо проверять, выполняется ли равенство assoc[assoc[X]] == assoc[X], т.е. зафиксировать
|
||||||
|
сложные связи.
|
||||||
|
|
||||||
|
Оказалось не все так просто с переименованием: надо будет нарисовать на бумажке,
|
||||||
|
что-то засыпаю уже. На сегодняшний день ничего не планирую (спать надо лечь рано,
|
||||||
|
т.к. завтра в 5 утра вставать), так что, пожалуй, закруглюсь и буду спать.
|
||||||
|
Сегодня еще вопросы с криостатом утрясти надо будет.
|
||||||
|
|
||||||
|
Пока засыпал, придумал, как правильно скорректировать алгоритм переименования:
|
||||||
|
скажем, переименовываем мы пиксель с изначальной меткой "X", имеющий новую
|
||||||
|
метку "Y" (т.е. assoc[X] != X) в "Z". В этом случае надо наоборот переименовать
|
||||||
|
Z в Y, а величины, больше Z, но <= Ncur декрементировать, затем декрементировать
|
||||||
|
и Ncur.
|
||||||
|
Все довольно просто, а очевидным
|
||||||
|
плюсом является то, что не надо многократно сканировать изображение для переименования
|
||||||
|
"по месту": это будет сделано на шаге 3.
|
||||||
|
|
||||||
|
Конечно, все познается в сравнении, поэтому сразу обливать дерьмом этот метод
|
||||||
|
нельзя: стоит попытаться его реализовать. А учитывая то, что все шаги, кроме второго,
|
||||||
|
превосходно параллелизуются (1, 3 - хоть по строкам, хоть по прямоугольным областям,
|
||||||
|
4 - по номерам объектов), на куде, пожалуй, он будет работать просто превосходно!
|
||||||
|
|
||||||
|
|
||||||
|
== еще мысли ==
|
||||||
|
Однако, меня все еще грызут сомнения: ведь по сути обнаружение связанных областей
|
||||||
|
подобно закрашиванию области, ограниченной контуром (в данном случае - "закрашивание"
|
||||||
|
нулями этой самой связанной области с "перенесением" соответствующих пикселей
|
||||||
|
во временное хранилище (а оттуда уже, по завершению закрашивания, - в соответствующее
|
||||||
|
подызображение).
|
||||||
|
|
||||||
|
Итак, если выбрать эффективный метод закрашивания областей, то обнаружение
|
||||||
|
сведется к следующему:
|
||||||
|
1. пробегаемся по точкам изображения, как только находим 1, "закрашиваем" всю
|
||||||
|
область нулями с "перенесением" ее во временную маску (равную размеру изображения)
|
||||||
|
и параллельным обновлением границ бокса;
|
||||||
|
2. выделяем для бокса память и переносим в него кусок из маски ("модифицированный"
|
||||||
|
шаг 4 из предыдущих размышлений);
|
||||||
|
3. сканируем изображение, начиная со следующей точки после точки из п.1.
|
||||||
|
|
||||||
|
Если опустить "волшебную" фразу "закрашиваем" из п.1, то изображение сканируется
|
||||||
|
не "три с половиной", а "полтора" раза. Т.о., если "закрашивание" будет
|
||||||
|
сканировать пиксели изображения лишь один раз, то получим весомый выигрыш.
|
||||||
|
Однако, тут же всплывает и минус: параллелится эта задача только разбиением
|
||||||
|
изображения на фреймы с последующим объединением оных. В принципе, если "закрашивание"
|
||||||
|
будет достаточно быстрым, то проблем не будет: все равно алгоритм будет работать
|
||||||
|
быстрее классического.
|
||||||
|
|
||||||
|
Итак, теперь остается прикинуть: что же за алгоритмы закрашивания есть.
|
||||||
|
Если верить википедии, то самый быстрый алгоритм основывается на закрашивании
|
||||||
|
области линиями с параллельной проверкой соседних сверху и снизу пикселей и
|
||||||
|
занесением в список координат пикселей, которые появляются после 0. По окончанию
|
||||||
|
сканирования линии, мы переходим к следующему пикселю в списке. И так до тех
|
||||||
|
пор, пока список не кончится. Причем, сканирование идет в двух направлениях!
|
||||||
|
|
||||||
|
Еще я наткнулся на какой-то "очень быстрый метод" маркирования связанных областей
|
||||||
|
от братьев-китайцев, но что-то даже после трехкратного прочтения я так и не понял:
|
||||||
|
то ли братья-китайцы что-то темнят, то ли я - идиот (конечно, возможны оба
|
||||||
|
варианта, но мне больше первый нравится).
|
||||||
|
|
||||||
|
Итак, хватит растекаться мысью по древу - пора переключаться в "медленный режим"
|
||||||
|
и реализовывать методы. В любом случае, закрашивание областей реализовать надо
|
||||||
|
(для начала - хотя бы 4-связанных, а о восьмисвязанных подумать, т.к. я что-то
|
||||||
|
с трудом себе представляю реальные объекты, ограниченные четким 4-связанным
|
||||||
|
контуром), поэтому стоит для начала на CPU с помощью openMP реализовать названные
|
||||||
|
два способа маркировки, да и сравнить их: даже если окажется, что второй способ
|
||||||
|
медленнее, он все равно понадобится для закрашивания.
|
||||||
|
|
||||||
|
|
||||||
|
// что-то у меня промежуточные всякие выкладки много времени занимают: сейчас вот
|
||||||
|
начал организовывать FIFO и подумал, что неплохо было бы туда и LIFO добавить...
|
||||||
|
|
||||||
|
|
||||||
|
== реализация перемаркировки ==
|
||||||
|
Реализуя перемаркировку, я наткнулся на кое-какие "косяки". В итоге "малюсенькая"
|
||||||
|
inline-функция для перемаркировки превратилась в громоздкого монстра.
|
||||||
|
Но надо еще протестировать на разных "картинках" ее поведение. Если проблем
|
||||||
|
с перемаркировкой не будет, можно будет переходить к завершающему этапу - заполнению.
|
||||||
|
|
||||||
|
Так как в процессе перемаркировки счетчик количества объектов скачет туда-сюда,
|
||||||
|
для завершающего - четвертого - этапа придется-таки использовать "полтора" прохода:
|
||||||
|
сначала пробежаться по изображению с заполнением величин соответствующих боксов,
|
||||||
|
а затем уже пробежаться по внутренностям каждого бокса, заполняя их маски.
|
||||||
|
|
||||||
|
Пожалуй, цикл по ремаркировке стоит вынести в отдельную функцию: тогда можно будет
|
||||||
|
искать и 4-связанные области без предварительной 4-фильтрации.
|
||||||
|
|
||||||
|
Проверка на случайно сгенерированных "картинках" показала, что алгоритм вроде бы
|
||||||
|
вполне рабочий. Надо бы добавить отбрасывание одиночных точек. Но что-то не
|
||||||
|
хочется мне утяжелять и без того уже тормозной и раздувшийся алгоритм.
|
||||||
|
Проще все-таки, наверное, сделать предварительную фильтрацию (по октетам)
|
||||||
|
"сжатого" изображения (как в FCfilter), а потом уже обрабатывать ее результат.
|
||||||
|
|
||||||
|
Кстати, сейчас прогнал поиск 8-связанных областей по изображению, отфильтрованному
|
||||||
|
функцией FCfilter, и до меня дошло, что все равно нужно писать отдельную функцию
|
||||||
|
поиска 4-связанных областей, т.к. вполне может быть так, что две 4-связанные
|
||||||
|
области соприкасаются по диагонали. При этом они являются разными объектами, но
|
||||||
|
поиск 8-связанных областей обзывает их одинаково (что понятно).
|
||||||
|
|
||||||
|
Для упрощения громоздких выкладок я сделал такой велосипед: внутренности некоторых
|
||||||
|
функций были вынесены в отдельные (многократно подключаемые) файлы. Перед подключением
|
||||||
|
файла для модификации устанавливается тот или иной флаг. В результате некоторое
|
||||||
|
количество условных операций отпадает. Правда, реализация перемаркировки
|
||||||
|
получилась у меня такой страшнючей, что мне лень ее оптимизировать подобным образом.
|
||||||
|
|
||||||
|
Перед тем, как выполнить поиск 4-связанных областей, я выполняю фильтрацию. Она
|
||||||
|
заодно отсекает и одиночные пиксели. А вот фильтрация для функции выделения
|
||||||
|
8-связанных областей заключается в том, что как раз только одиночные пиксели
|
||||||
|
и отсекаются. Жалею сейчас, что пока был в ИПФ, не нагуглил, как наиболее оптимально
|
||||||
|
их фильтровать, поэтому опять буду изобретать велосипед.
|
||||||
|
Фильтрация одиночных точек на "упакованном" изображении при помощи логических
|
||||||
|
операций, как я говорил выше, использует огромное количество промежуточных
|
||||||
|
вычислений, поэтому я даже и браться не буду за нее: выигрыш производительности
|
||||||
|
получится сомнительным. Поэтому, пожалуй, проще всего будет воткнуть эту проверку
|
||||||
|
в функцию поиска связанных областей.
|
||||||
|
|
||||||
|
Красотищща! Оно работает!!!
|
||||||
|
|
||||||
|
Рано радовался: запустил тест (случайная картинка 2000х2000): получилось, что
|
||||||
|
cc4 работает почти в 2 раза медленней, чем cc8!!! Как? А ведь у cc8 значительно
|
||||||
|
больше проверок... Черт знает что! Да и вообще не понравилось мне время
|
||||||
|
выполнения: 13.3 секунд у cc4 и 7.8 у cc8. Кстати, я еще не реализовал
|
||||||
|
шаг 4! При этом если из cc4 выкинуть функцию `FC_filter`, производительность
|
||||||
|
возрастает еще на пару секунд (ну, это,
|
||||||
|
наверное, из-за того, что приходится лишние пиксели проверять).
|
||||||
|
|
||||||
|
Я подозреваю, что виновата перемаркировка, занимающая тем больше времени,
|
||||||
|
чем больше областей обнаружено на изображении. Сейчас воткну тайминги в cclabel.
|
||||||
|
Тайминги показали, что действительно шаг 2 занимает на 4 порядка больше времени,
|
||||||
|
чем остальные шаги. А ведь сканирование-то идет единожды!
|
||||||
|
Отключив функцию ремаркировки, я получил крайне высокую производительность.
|
||||||
|
Глянул количество первично обнаруженных областей - их больше 200 тысяч!
|
||||||
|
Понятное дело, что если каждый раз при перемаркировке пробегаться по 200000
|
||||||
|
значений, а ремаркировок получаем почти столько же, производительность выйдет
|
||||||
|
вообще никакая!
|
||||||
|
|
||||||
|
В общем, надо думать, как оптимизировать это узкое место. Параллелизация
|
||||||
|
дала прирост производительности всего лишь в полтора раза. Я, правда, еще заметил,
|
||||||
|
что у меня количество итераций постоянно - по всем элементам, хотя стоит
|
||||||
|
проверять лишь те, которые уже пройдены. Скорректировал, получил прирост
|
||||||
|
производительности всего-то в полтора раза у cc4 и никакого прироста у cc8!
|
||||||
|
|
||||||
|
Уменьшив содержание единиц и нулей на изображении от 50/50 до 10/90, я получил
|
||||||
|
поразительно низкие цифры для cc4: полторы сотни миллисекунд! Время cc8 получилось
|
||||||
|
в районе 1с. Даже не знаю, нормально ли это: надо будет сравнить с другими
|
||||||
|
алгоритмами (хотя бы с лептоникой).
|
||||||
|
Уже сравнение с октавой показало, что у меня полная лажа: для тех же 50/50
|
||||||
|
октава дает 20мс! Зато для 10/90 у меня быстрей, чем в октаве (но октава
|
||||||
|
считает и одиночные точки :( ).
|
||||||
|
|
||||||
|
Проверка на очень большом количестве единиц показала, что считать все-таки надо
|
||||||
|
не до текущего значения (я про цикл с перемаркировкой) ведь нижняя строчкае
|
||||||
|
уже может быть ремаркирована -> получаем косячок-с. Чтобы его не было, добавлю
|
||||||
|
"про запас" половину ширины строки (больше объектов все равно быть не может).
|
||||||
|
|
||||||
|
Я долго думал, как бы это на графах реализовать, но что-то так и не придумал.
|
||||||
|
Посмотрел на время - уже начало 11-го. Пора баиньки, а то завтра в 4:30 вставать.
|
||||||
|
|
||||||
|
== другой метод ==
|
||||||
|
Пока сидел в аэропорту НН, было время почитать статью китайцев. Действительно,
|
||||||
|
интересный способ: мой метод (теоретически) будет хорош на небольшом количестве
|
||||||
|
больших объектов, а их метод - на большом количестве малых.
|
||||||
|
Принцип почти тот же, что и в моем случае, но:
|
||||||
|
- шаги 1 и 2 объединены: на стадии именования меток происходит и ремаркировка;
|
||||||
|
- второй проход (который у меня - третий) такой же, как у меня - переименование
|
||||||
|
участков по скорректированным меткам.
|
||||||
|
Правда, китайцы умолчали, как они создавали список меток: если его создавать
|
||||||
|
динамически (как список, динамический массив или даже дерево), то все операции
|
||||||
|
над списком будут довольно-таки дорогими, поэтому есть смысл просто выделить
|
||||||
|
заранее достаточное количество памяти. Учитывая то, что объектов на изображении
|
||||||
|
ну никак не может быть больше, чем 1/6 размера изображения, можно "на авось"
|
||||||
|
выделить 1/4 размера изображения под массив ремаркировок.
|
||||||
|
|
||||||
|
Еще одним отличием является метод сканирования: сканируются все пиксели, но
|
||||||
|
в зависимости от того, был ли ненулевой пиксель до текущего, или его не было,
|
||||||
|
производятся разные действия: если точка новая, производится проверка ТРЕХ точек
|
||||||
|
над ней (точки снизу не проверяются, понятное дело, т.к. происходит сканирование
|
||||||
|
всех точек) с соответствующим именованием текущей точки и ремаркировкой, если мы
|
||||||
|
натыкаемся на ситуацию, когда над точкой ничего нет, а вверху справа и слева -
|
||||||
|
разные метки. Если же до текущей точки уже была точка, нам нужно лишь проверить,
|
||||||
|
что находится в правом верхнем углу. Если там ничего нет, мы просто даем точке
|
||||||
|
номер соседней слева, если же есть - все равно даем тот же номер, но еще и производим
|
||||||
|
процедуру ремаркировки.
|
||||||
|
|
||||||
|
Благодаря такому методу сканирования (теоретически) повышается скорость: ремаркировки
|
||||||
|
необходимо производить значительно реже. В общем, надо бы проверить этот метод.
|
||||||
|
|
||||||
|
Описанная выше процедура - для маркировки 8-связанных областей, если же нам нужно
|
||||||
|
маркировать 4-связанные области, мы просто проверяем лишь точку НАД нашей.
|
||||||
|
|
||||||
|
Да уж, попытался я было что-нибудь "покодить", сидя в аэропорту. Оказалось, что это
|
||||||
|
не так-то и просто: без мыши и нормальной клавиатуры ну совсем невозможно работать!
|
||||||
|
Да еще и экран ноутбука перевешивает - бук так и норовит кувыркнуться с колен.
|
||||||
|
В общем, откладываю до дома.
|
||||||
|
|
||||||
|
Черт бы подрал этот аэропорт: wifi как бы номинально есть, а реально - не работает,
|
||||||
|
собака! Вроде бы соединяет, но скорость очень близка к нулю: tracepath дает
|
||||||
|
аж секунды! В общем, на втором этаже жизни нет.
|
||||||
|
|
||||||
|
Все-таки сделал более-менее реализацию. Работает, но нужно еще внимательно
|
||||||
|
просмотреть функцию ремаркировки: эта собака неправильно считает количество
|
||||||
|
найденных объектов!
|
||||||
|
|
||||||
|
Бульбец! Самолет задерживают на полтора часа! Буду дописывать...
|
||||||
428
erosion-dilation/binmorph.c
Normal file
428
erosion-dilation/binmorph.c
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
/*
|
||||||
|
* binmorph.c - functions for morphological operations on binary image
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
TEST (10000 dilations / erosions / substitutions) for i5-3210M CPU @ 2.50GHz x2cores x2hyper
|
||||||
|
1. no openmp:
|
||||||
|
* 28.8 / 29.2 / 14.8
|
||||||
|
2. 2 threads:
|
||||||
|
* 17.0 / 17.9 / 8.8
|
||||||
|
3. 4 threads:
|
||||||
|
* 17.0 / 17.5 / 8.8
|
||||||
|
4. 8 threads:
|
||||||
|
* 17.0 / 17.7 / 8.9
|
||||||
|
*
|
||||||
|
option -O3 gives for nthreads = 4:
|
||||||
|
* 6.9 / 6.9 / 2.9
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "binmorph.h"
|
||||||
|
//#include "fifo.h"
|
||||||
|
|
||||||
|
#ifdef OMP
|
||||||
|
#ifndef OMP_NUM_THREADS
|
||||||
|
#define OMP_NUM_THREADS 4
|
||||||
|
#endif
|
||||||
|
#define Stringify(x) #x
|
||||||
|
#define OMP_FOR(x) _Pragma(Stringify(omp parallel for x))
|
||||||
|
#else
|
||||||
|
#define OMP_FOR(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// global arrays for erosion/dilation masks
|
||||||
|
unsigned char *ER = NULL, *DIL = NULL;
|
||||||
|
bool __Init_done = false; // == true if arrays are inited
|
||||||
|
|
||||||
|
/*
|
||||||
|
* =================== AUXILIARY FUNCTIONS ===================>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function for different purposes that need to know time intervals
|
||||||
|
* @return double value: time in seconds
|
||||||
|
*/
|
||||||
|
double dtime(){
|
||||||
|
double t;
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory allocation with control
|
||||||
|
* @param N - number of elements
|
||||||
|
* @param S - size of one element
|
||||||
|
* @return allocated memory or die
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function inits masks arrays for erosion and dilation
|
||||||
|
* You may call it yourself or it will be called when one of
|
||||||
|
* `erosion` or `dilation` functions will be ran first time
|
||||||
|
*/
|
||||||
|
void morph_init(){
|
||||||
|
if(__Init_done) return;
|
||||||
|
int i;//,j;
|
||||||
|
//bool a[8], b[8], c[9];
|
||||||
|
ER = Malloc(256, 1);
|
||||||
|
DIL = Malloc(256, 1);
|
||||||
|
for(i = 0; i < 256; i++){
|
||||||
|
/*ER[i] = DIL[i] = 0;
|
||||||
|
for(j = 0; j < 8; j++){
|
||||||
|
a[j] = (i >> j) & 1;
|
||||||
|
b[j] = a[j];
|
||||||
|
c[j] = a[j];
|
||||||
|
}
|
||||||
|
for(j = 1; j < 7; j++)
|
||||||
|
if(!a[j])
|
||||||
|
b[j+1] = b[j-1]= false;
|
||||||
|
else
|
||||||
|
c[j] = c[j-1] = c[j+1] = true;
|
||||||
|
b[1] &= a[0]; b[6] &= a[7];
|
||||||
|
c[1] |= a[0]; c[6] |= a[7];
|
||||||
|
for(j = 0; j < 8; j++){
|
||||||
|
ER[i] |= b[j] << j;
|
||||||
|
DIL[i] |= c[j] << j;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
ER[i] = i & ((i << 1) | 1) & ((i >> 1) | (0x80)); // don't forget that << and >> set borders to zero
|
||||||
|
DIL[i] = i | (i << 1) | (i >> 1);
|
||||||
|
}
|
||||||
|
__Init_done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print out small "packed" images as matrix of "0" and "1"
|
||||||
|
* @param i (i) - image
|
||||||
|
* @param W, H - size of image
|
||||||
|
*/
|
||||||
|
void printC(unsigned char *i, int W, int H){
|
||||||
|
int x,y,b;
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
for(x = 0; x < W; x++, i++){
|
||||||
|
unsigned char c = *i;
|
||||||
|
for(b = 0; b < 8; b++)
|
||||||
|
printf("%c", c << b & 0x80 ? '1' : '0');
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print out small "unpacked" images as almost equiaxed matrix of ".." and "##"
|
||||||
|
* @param img (i) - image
|
||||||
|
* @param W, H - size of image
|
||||||
|
*/
|
||||||
|
void printB(bool *img, int W, int H){
|
||||||
|
int i, j;
|
||||||
|
for(j = 0; j < W; j++)
|
||||||
|
if(j % 10 == 1){ printf("%02d", j-1); printf(" ");}
|
||||||
|
printf("\n");
|
||||||
|
for(i = 0; i < H; i++){
|
||||||
|
printf("%2d ",i);
|
||||||
|
for(j = 0; j < W; j++)
|
||||||
|
printf("%s", (*img++) ? "##" : "..");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <=================== AUXILIARY FUNCTIONS ===================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* =================== CONVERT IMAGE TYPES ===================>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert boolean image into pseudo-packed (1 char == 8 pixels)
|
||||||
|
* @param im (i) - image to convert
|
||||||
|
* @param W, H - size of image im (must be larger than 1)
|
||||||
|
* @param W_0 (o) - (stride) new width of image
|
||||||
|
* @return allocated memory area with "packed" image
|
||||||
|
*/
|
||||||
|
unsigned char *bool2char(bool *im, int W, int H, int *W_0){
|
||||||
|
if(W < 2 || H < 2) errx(1, "bool2char: image size too small");
|
||||||
|
int y, W0 = (W + 7) / 8;
|
||||||
|
unsigned char *ret = Malloc(W0 * H, 1);
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
int x, i, X;
|
||||||
|
bool *ptr = &im[y*W];
|
||||||
|
unsigned char *rptr = &ret[y*W0];
|
||||||
|
for(x = 0, X = 0; x < W0; x++, rptr++){
|
||||||
|
for(i = 7; i > -1 && X < W; i--, X++, ptr++){
|
||||||
|
*rptr |= *ptr << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(W_0) *W_0 = W0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert "packed" image into boolean
|
||||||
|
* @param image (i) - input image
|
||||||
|
* @param W, H, W_0 - size of image and width of "packed" image
|
||||||
|
* @return allocated memory area with "unpacked" image
|
||||||
|
*/
|
||||||
|
bool *char2bool(unsigned char *image, int W, int H, int W_0){
|
||||||
|
int y;
|
||||||
|
bool *ret = Malloc(W * H, sizeof(bool));
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
int x, X, i;
|
||||||
|
bool *optr = &ret[y*W];
|
||||||
|
unsigned char *iptr = &image[y*W_0];
|
||||||
|
for(x = 0, X = 0; x < W_0; x++, iptr++)
|
||||||
|
for(i = 7; i > -1 && X < W; i--, X++, optr++){
|
||||||
|
*optr = (*iptr >> i) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert "packed" image into size_t array for conncomp procedure
|
||||||
|
* @param image (i) - input image
|
||||||
|
* @param W, H, W_0 - size of image and width of "packed" image
|
||||||
|
* @return allocated memory area with copy of an image
|
||||||
|
*/
|
||||||
|
size_t *char2st(unsigned char *image, int W, int H, int W_0){
|
||||||
|
int y;
|
||||||
|
size_t *ret = Malloc(W * H, sizeof(size_t));
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
int x, X, i;
|
||||||
|
size_t *optr = &ret[y*W];
|
||||||
|
unsigned char *iptr = &image[y*W_0];
|
||||||
|
for(x = 0, X = 0; x < W_0; x++, iptr++)
|
||||||
|
for(i = 7; i > -1 && X < W; i--, X++, optr++){
|
||||||
|
*optr = (*iptr >> i) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <=================== CONVERT IMAGE TYPES ===================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* =================== MORPHOLOGICAL OPERATIONS ===================>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all non-4-connected pixels
|
||||||
|
* @param image (i) - input image
|
||||||
|
* @param W, H - size of image
|
||||||
|
* @return allocated memory area with converted input image
|
||||||
|
*/
|
||||||
|
unsigned char *FC_filter(unsigned char *image, int W, int H){
|
||||||
|
if(W < 2 || H < 2) errx(1, "4-connect: image size too small");
|
||||||
|
unsigned char *ret = Malloc(W*H, 1);
|
||||||
|
int y = 0, w = W-1, h = H-1;
|
||||||
|
// top of image, y = 0
|
||||||
|
#define IM_UP
|
||||||
|
#include "fc_filter.h"
|
||||||
|
#undef IM_UP
|
||||||
|
// mid of image, y = 1..h-1
|
||||||
|
#include "fc_filter.h"
|
||||||
|
// image bottom, y = h
|
||||||
|
y = h;
|
||||||
|
#define IM_DOWN
|
||||||
|
#include "fc_filter.h"
|
||||||
|
#undef IM_DOWN
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make morphological operation of dilation
|
||||||
|
* @param image (i) - input image
|
||||||
|
* @param W, H - size of image
|
||||||
|
* @return allocated memory area with dilation of input image
|
||||||
|
*/
|
||||||
|
unsigned char *dilation(unsigned char *image, int W, int H){
|
||||||
|
if(W < 2 || H < 2) errx(1, "Dilation: image size too small");
|
||||||
|
if(!__Init_done) morph_init();
|
||||||
|
unsigned char *ret = Malloc(W*H, 1);
|
||||||
|
int y = 0, w = W-1, h = H-1;
|
||||||
|
// top of image, y = 0
|
||||||
|
#define IM_UP
|
||||||
|
#include "dilation.h"
|
||||||
|
#undef IM_UP
|
||||||
|
// mid of image, y = 1..h-1
|
||||||
|
#include "dilation.h"
|
||||||
|
// image bottom, y = h
|
||||||
|
y = h;
|
||||||
|
#define IM_DOWN
|
||||||
|
#include "dilation.h"
|
||||||
|
#undef IM_DOWN
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make morphological operation of erosion
|
||||||
|
* @param image (i) - input image
|
||||||
|
* @param W, H - size of image
|
||||||
|
* @return allocated memory area with erosion of input image
|
||||||
|
*/
|
||||||
|
unsigned char *erosion(unsigned char *image, int W, int H){
|
||||||
|
if(W < 2 || H < 2) errx(1, "Erosion: image size too small");
|
||||||
|
if(!__Init_done) morph_init();
|
||||||
|
unsigned char *ret = Malloc(W*H, 1);
|
||||||
|
int y, w = W-1, h = H-1;
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 1; y < h; y++){ // reset first & last rows of image
|
||||||
|
unsigned char *iptr = &image[W*y];
|
||||||
|
unsigned char *optr = &ret[W*y];
|
||||||
|
unsigned char p = ER[*iptr] & 0x7f & iptr[-W] & iptr[W];
|
||||||
|
int x;
|
||||||
|
if(!(iptr[1] & 0x80)) p &= 0xfe;
|
||||||
|
*optr++ = p;
|
||||||
|
iptr++;
|
||||||
|
for(x = 1; x < w; x++, iptr++, optr++){
|
||||||
|
p = ER[*iptr] & iptr[-W] & iptr[W];
|
||||||
|
if(!(iptr[-1] & 1)) p &= 0x7f;
|
||||||
|
if(!(iptr[1] & 0x80)) p &= 0xfe;
|
||||||
|
*optr = p;
|
||||||
|
}
|
||||||
|
p = ER[*iptr] & 0xfe & iptr[-W] & iptr[W];
|
||||||
|
if(!(iptr[-1] & 1)) p &= 0x7f;
|
||||||
|
*optr++ = p;
|
||||||
|
iptr++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <=================== MORPHOLOGICAL OPERATIONS ===================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* =================== LOGICAL OPERATIONS ===================>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical AND of two images
|
||||||
|
* @param im1, im2 (i) - two images
|
||||||
|
* @param W, H - their size (of course, equal for both images)
|
||||||
|
* @return allocated memory area with image = (im1 AND im2)
|
||||||
|
*/
|
||||||
|
unsigned char *imand(unsigned char *im1, unsigned char *im2, int W, int H){
|
||||||
|
unsigned char *ret = Malloc(W*H, 1);
|
||||||
|
int y;
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
int x, S = y*W;
|
||||||
|
unsigned char *rptr = &ret[S], *p1 = &im1[S], *p2 = &im2[S];
|
||||||
|
for(x = 0; x < W; x++)
|
||||||
|
*rptr++ = *p1++ & *p2++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substitute image 2 from image 1: reset to zero all bits of image 1 which set to 1 on image 2
|
||||||
|
* @param im1, im2 (i) - two images
|
||||||
|
* @param W, H - their size (of course, equal for both images)
|
||||||
|
* @return allocated memory area with image = (im1 AND (!im2))
|
||||||
|
*/
|
||||||
|
unsigned char *substim(unsigned char *im1, unsigned char *im2, int W, int H){
|
||||||
|
unsigned char *ret = Malloc(W*H, 1);
|
||||||
|
int y;
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
int x, S = y*W;
|
||||||
|
unsigned char *rptr = &ret[S], *p1 = &im1[S], *p2 = &im2[S];
|
||||||
|
for(x = 0; x < W; x++)
|
||||||
|
*rptr++ = *p1++ & (~*p2++);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <=================== LOGICAL OPERATIONS ===================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* =================== CONNECTED COMPONENTS LABELING ===================>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <=================== CONNECTED COMPONENTS LABELING ===================
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#undef DBG
|
||||||
|
#undef EBUG
|
||||||
|
#define DBG(...)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* label 4-connected components on image
|
||||||
|
* (slow algorythm, but easy to parallel)
|
||||||
|
*
|
||||||
|
* @param I (i) - image ("packed")
|
||||||
|
* @param W,H,W_0 - size of the image (W - width in pixels, W_0 - width in octets)
|
||||||
|
* @param Nobj (o) - number of objects found
|
||||||
|
* @return an array of labeled components
|
||||||
|
*/
|
||||||
|
CCbox *cclabel4(unsigned char *Img, int W, int H, int W_0, size_t *Nobj){
|
||||||
|
unsigned char *I = FC_filter(Img, W_0, H);
|
||||||
|
#include "cclabling.h"
|
||||||
|
FREE(I);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// label 8-connected components, look cclabel4
|
||||||
|
CCbox *cclabel8(unsigned char *I, int W, int H, int W_0, size_t *Nobj){
|
||||||
|
//unsigned char *I = EC_filter(Img, W_0, H);
|
||||||
|
#define LABEL_8
|
||||||
|
#include "cclabling.h"
|
||||||
|
#undef LABEL_8
|
||||||
|
// FREE(I);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCbox *cclabel8_1(unsigned char *I, int W, int H, int W_0, size_t *Nobj){
|
||||||
|
#define LABEL_8
|
||||||
|
#include "cclabling_1.h"
|
||||||
|
#undef LABEL_8
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <=================== template ===================>
|
||||||
|
*/
|
||||||
86
erosion-dilation/binmorph.h
Normal file
86
erosion-dilation/binmorph.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* binmorph.h
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __EROSION_DILATION_H__
|
||||||
|
#define __EROSION_DILATION_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef EBUG
|
||||||
|
#ifndef DBG
|
||||||
|
#define DBG(...) do{fprintf(stderr, __VA_ARGS__);}while(0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _U_ __attribute__((__unused__))
|
||||||
|
|
||||||
|
#ifndef Malloc
|
||||||
|
#define Malloc my_alloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FREE
|
||||||
|
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _
|
||||||
|
#define _(X) X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// auxiliary functions
|
||||||
|
void *my_alloc(size_t N, size_t S);
|
||||||
|
double dtime();
|
||||||
|
void printC(unsigned char *i, int W, int H);
|
||||||
|
void printB(bool *i, int W, int H);
|
||||||
|
void morph_init(); // there's no need to run it by hands, but your will is the law
|
||||||
|
|
||||||
|
// convert image types
|
||||||
|
unsigned char *bool2char(bool *im, int W, int H, int *stride);
|
||||||
|
bool *char2bool(unsigned char *image, int W, int H, int W_0);
|
||||||
|
size_t *char2st(unsigned char *image, int W, int H, int W_0);
|
||||||
|
|
||||||
|
// morphological operations
|
||||||
|
unsigned char *dilation(unsigned char *image, int W, int H);
|
||||||
|
unsigned char *erosion(unsigned char *image, int W, int H);
|
||||||
|
unsigned char *FC_filter(unsigned char *image, int W, int H);
|
||||||
|
|
||||||
|
// logical operations
|
||||||
|
unsigned char *imand(unsigned char *im1, unsigned char *im2, int W, int H);
|
||||||
|
unsigned char *substim(unsigned char *im1, unsigned char *im2, int W, int H);
|
||||||
|
|
||||||
|
// conncomp
|
||||||
|
// this is a box structure containing one object; data is aligned by original image bytes!
|
||||||
|
typedef struct {
|
||||||
|
unsigned char *data; // pattern data in "packed" format
|
||||||
|
int x, // x coordinate of LU-pixel of box in "unpacked" image (by pixels)
|
||||||
|
y, // y -//-
|
||||||
|
x_0; // x coordinate in "packed" image (morph operations should work with it)
|
||||||
|
size_t N;// number of component, starting from 1
|
||||||
|
} CCbox;
|
||||||
|
|
||||||
|
CCbox *cclabel4(unsigned char *I, int W, int H, int W_0, size_t *Nobj);
|
||||||
|
CCbox *cclabel8(unsigned char *I, int W, int H, int W_0, size_t *Nobj);
|
||||||
|
|
||||||
|
CCbox *cclabel8_1(unsigned char *I, int W, int H, int W_0, size_t *Nobj);
|
||||||
|
#endif // __EROSION_DILATION_H__
|
||||||
|
|
||||||
|
|
||||||
204
erosion-dilation/cclabling.h
Normal file
204
erosion-dilation/cclabling.h
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* cclabling.h - inner part of functions cclabel4 and cclabel8
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
double t0 = dtime();
|
||||||
|
CCbox *ret = NULL;
|
||||||
|
size_t N _U_ = 0, Ncur = 0;
|
||||||
|
size_t *labels = char2st(I, W, H, W_0);
|
||||||
|
int y;
|
||||||
|
#ifdef EBUG
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
if(*ptr) printf("%02zx", *ptr);
|
||||||
|
else printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
FREE(labels); return ret;
|
||||||
|
#endif
|
||||||
|
printf("time for char2st: %gs\n", dtime()-t0);
|
||||||
|
t0 = dtime();
|
||||||
|
// Step 1: mark neighbours by strings
|
||||||
|
size_t *ptr = labels;
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
bool found = false;
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
if(!*ptr){ found = false; continue;}
|
||||||
|
if(!found){
|
||||||
|
found = true;
|
||||||
|
++Ncur;
|
||||||
|
}
|
||||||
|
*ptr = Ncur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n\ntime for step1: %gs (Ncur=%zd)\n", dtime()-t0, Ncur);
|
||||||
|
t0 = dtime();
|
||||||
|
DBG("Initial mark\n");
|
||||||
|
#ifdef EBUG
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
if(*ptr) printf("%02zx", *ptr);
|
||||||
|
else printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Step 2: fill associative array with remarking
|
||||||
|
DBG("remarking\n");
|
||||||
|
N = Ncur+1; // size of markers array (starting from 1)
|
||||||
|
size_t i, *assoc = Malloc(N, sizeof(size_t));
|
||||||
|
for(i = 0; i < N; i++) assoc[i] = i; // primary initialisation
|
||||||
|
// now we should scan image again to rebuild enumeration
|
||||||
|
// THIS PROCESS AVOID PARALLELISATION???
|
||||||
|
Ncur = 0;
|
||||||
|
size_t h = H-1
|
||||||
|
#ifdef LABEL_8
|
||||||
|
,w = W-1;
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
inline void remark(size_t old, size_t *newv){
|
||||||
|
size_t new = *newv;
|
||||||
|
if(assoc[old] == new){
|
||||||
|
DBG("(%zd)\n", new);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DBG("[%zx], %zx -> %zx ", Ncur, old, new);
|
||||||
|
if(assoc[old] == old){ // remark non-remarked value
|
||||||
|
assoc[old] = new;
|
||||||
|
DBG("\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// value was remarked -> we have to remark "new" to assoc[old]
|
||||||
|
// and decrement all marks, that are greater than "new"
|
||||||
|
size_t ao = assoc[old];
|
||||||
|
DBG("(remarked to %zx) ", assoc[old]);
|
||||||
|
// now we must check assoc[old]: if it is < new -> change *newv, else remark assoc[old]
|
||||||
|
if(ao < new)
|
||||||
|
*newv = ao;
|
||||||
|
else{
|
||||||
|
size_t x = ao; ao = new; new = x; // swap values
|
||||||
|
}
|
||||||
|
int xx = old + W / 2;
|
||||||
|
if(xx > N) xx = N;
|
||||||
|
OMP_FOR()
|
||||||
|
for(int i = 1; i <= xx; i++){
|
||||||
|
size_t m = assoc[i];
|
||||||
|
if(m < new) continue;
|
||||||
|
if(m == new){
|
||||||
|
assoc[i] = ao;
|
||||||
|
DBG("remark %x (%zd) to %zx ", i, m, ao);
|
||||||
|
}else if(m <= Ncur){
|
||||||
|
assoc[i]--;
|
||||||
|
DBG("decr %x: %zx, ", i, assoc[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("\n");
|
||||||
|
Ncur--;
|
||||||
|
}
|
||||||
|
for(y = 0; y < H; y++){ // FIXME!!!! throw out that fucking "if" checking coordinates!!!
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
bool found = false;
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
size_t curval = *ptr;
|
||||||
|
if(!curval){ found = false; continue;}
|
||||||
|
if(found) continue; // we go through remarked pixels
|
||||||
|
found = true;
|
||||||
|
DBG("curval: %zx ", curval);
|
||||||
|
// now check neighbours in upper and lower string:
|
||||||
|
size_t *U = (y) ? &ptr[-W] : NULL;
|
||||||
|
size_t *D = (y < h) ? &ptr[W] : NULL;
|
||||||
|
size_t upmark = 0; // mark from line above
|
||||||
|
if(U){
|
||||||
|
#ifdef LABEL_8
|
||||||
|
if(x && U[-1]){ // there is something in upper left corner -> make remark by its value
|
||||||
|
upmark = assoc[U[-1]];
|
||||||
|
}else // check point above only if there's nothing in left up
|
||||||
|
#endif
|
||||||
|
if(U[0]) upmark = assoc[U[0]];
|
||||||
|
#ifdef LABEL_8
|
||||||
|
if(x < w) if(U[1]){ // there's something in upper right
|
||||||
|
if(upmark){ // to the left of it was pixels
|
||||||
|
remark(U[1], &upmark);
|
||||||
|
}else
|
||||||
|
upmark = assoc[U[1]];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if(!upmark){ // there's nothing above - set current pixel to incremented counter
|
||||||
|
#ifdef LABEL_8 // check, whether pixel is not single
|
||||||
|
if( !(x && ((D && D[-1]) || ptr[-1])) // no left neighbours
|
||||||
|
&& !(x < w && ((D && D[1]) || ptr[1])) // no right neighbours
|
||||||
|
&& !(D && D[0])){ // no neighbour down
|
||||||
|
*ptr = 0; // erase this hermit!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
upmark = ++Ncur;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now remark DL and DR corners (bottom pixel will be checked on next line)
|
||||||
|
if(D){
|
||||||
|
#ifdef LABEL_8
|
||||||
|
if(x && D[-1]){
|
||||||
|
remark(D[-1], &upmark);
|
||||||
|
}
|
||||||
|
if(x < w && D[1]){
|
||||||
|
remark(D[1], &upmark);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(D[0]) remark(D[0], &upmark);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// remark current
|
||||||
|
remark(curval, &upmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("time for step 2: %gs, found %zd objects\n", dtime()-t0, Ncur);
|
||||||
|
t0 = dtime();
|
||||||
|
// Step 3: rename markers
|
||||||
|
DBG("rename markers\n");
|
||||||
|
// first correct complex assotiations in assoc
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
size_t p = *ptr;
|
||||||
|
if(!p){continue;}
|
||||||
|
*ptr = assoc[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("time for step 3: %gs\n", dtime()-t0);
|
||||||
|
#ifdef EBUG
|
||||||
|
printf("\n\n");
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
if(*ptr) printf("%02zx", *ptr);
|
||||||
|
else printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FREE(assoc);
|
||||||
|
FREE(labels);
|
||||||
170
erosion-dilation/cclabling_1.h
Normal file
170
erosion-dilation/cclabling_1.h
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* cclabling_1.h - inner part of functions cclabel4_1 and cclabel8_1
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
double t0 = dtime();
|
||||||
|
|
||||||
|
CCbox *ret = NULL;
|
||||||
|
size_t N = 0, // current label
|
||||||
|
Ntot = 0, // number of found objects
|
||||||
|
Nmax = W*H/4; // max number of labels
|
||||||
|
size_t *labels = char2st(I, W, H, W_0);
|
||||||
|
int w = W - 1;
|
||||||
|
printf("time for char2st: %gs\n", dtime()-t0);
|
||||||
|
int y;
|
||||||
|
size_t *assoc = Malloc(Nmax, sizeof(size_t)); // allocate memory for "remark" array
|
||||||
|
inline void remark(size_t old, size_t *newv){
|
||||||
|
size_t new = *newv;
|
||||||
|
if(old == new || assoc[old] == new || assoc[new] == old){
|
||||||
|
DBG("(%zd)\n", new);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DBG("[cur: %zx, tot: %zd], %zx -> %zx ", N, Ntot, old, new);
|
||||||
|
if(!assoc[old]){ // try to remark non-marked value
|
||||||
|
assoc[old] = new;
|
||||||
|
DBG("\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// value was remarked -> we have to remark "new" to assoc[old]
|
||||||
|
// and decrement all marks, that are greater than "new"
|
||||||
|
size_t ao = assoc[old];
|
||||||
|
DBG("(remarked to %zx) ", ao);
|
||||||
|
DBG(" {old: %zd, new: %zd, a[o]=%zd, a[n]=%zd} ", old, new, assoc[old], assoc[new]);
|
||||||
|
// now we must check assoc[old]: if it is < new -> change *newv, else remark assoc[old]
|
||||||
|
if(ao < new)
|
||||||
|
*newv = ao;
|
||||||
|
else{
|
||||||
|
size_t x = ao; ao = new; new = x; // swap values
|
||||||
|
}
|
||||||
|
int m = (old > new) ? old : new;
|
||||||
|
int xx = m + W / 2;
|
||||||
|
if(xx > Nmax) xx = Nmax;
|
||||||
|
DBG(" [[xx=%d]] ", xx);
|
||||||
|
OMP_FOR()
|
||||||
|
for(int i = 1; i <= xx; i++){
|
||||||
|
size_t m = assoc[i];
|
||||||
|
if(m < new) continue;
|
||||||
|
if(m == new){
|
||||||
|
assoc[i] = ao;
|
||||||
|
DBG("remark %x (%zd) to %zx ", i, m, ao);
|
||||||
|
}else{
|
||||||
|
assoc[i]--;
|
||||||
|
DBG("decr %x: %zx, ", i, assoc[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG("\n");
|
||||||
|
Ntot--;
|
||||||
|
}
|
||||||
|
t0 = dtime();
|
||||||
|
size_t *ptr = labels;
|
||||||
|
for(y = 0; y < H; y++){ // FIXME!!!! throw out that fucking "if" checking coordinates!!!
|
||||||
|
bool found = false;
|
||||||
|
size_t curmark = 0; // mark of pixel to the left
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
size_t curval = *ptr;
|
||||||
|
if(!curval){ found = false; continue;}
|
||||||
|
size_t *U = (y) ? &ptr[-W] : NULL;
|
||||||
|
size_t upmark = 0; // mark from line above
|
||||||
|
if(!found){ // new pixel, check neighbours above
|
||||||
|
found = true;
|
||||||
|
// now check neighbours in upper string:
|
||||||
|
if(U){
|
||||||
|
//#ifdef LABEL_8
|
||||||
|
if(x && U[-1]){ // there is something in upper left corner -> use its value
|
||||||
|
upmark = U[-1];
|
||||||
|
}else // check point above only if there's nothing in left up
|
||||||
|
//#endif
|
||||||
|
if(U[0]) upmark = U[0];
|
||||||
|
//#ifdef LABEL_8
|
||||||
|
if(x < w && U[1]){ // there's something in upper right
|
||||||
|
if(upmark){ // to the left of it was pixels
|
||||||
|
remark(U[1], &upmark);
|
||||||
|
}else
|
||||||
|
upmark = U[1];
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
if(!upmark){ // there's nothing above - set current pixel to incremented counter
|
||||||
|
#ifdef LABEL_8 // check, whether pixel is not single
|
||||||
|
size_t *D = (y < w) ? &ptr[W] : NULL;
|
||||||
|
if( !(x && ((D && D[-1]) || ptr[-1])) // no left neighbours
|
||||||
|
&& !(x < w && ((D && D[1]) || ptr[1])) // no right neighbours
|
||||||
|
&& !(D && D[0])){ // no neighbour down
|
||||||
|
*ptr = 0; // erase this hermit!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
upmark = ++N;
|
||||||
|
Ntot++;
|
||||||
|
assoc[upmark] = upmark; // refresh "assoc"
|
||||||
|
}
|
||||||
|
*ptr = upmark;
|
||||||
|
curmark = upmark;
|
||||||
|
//remark(curval, &upmark);
|
||||||
|
}else{ // there was something to the left -> we must chek only U[1]
|
||||||
|
if(U){
|
||||||
|
if(x < w && U[1]){ // there's something in upper right
|
||||||
|
remark(U[1], &curmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*ptr = curmark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("time for step 2: %gs, found %zd objects\n", dtime()-t0, Ntot);
|
||||||
|
DBG("Initial mark\n");
|
||||||
|
#ifdef EBUG
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
if(*ptr) printf("%02zx", *ptr);
|
||||||
|
else printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
t0 = dtime();
|
||||||
|
// Step 2: rename markers
|
||||||
|
DBG("rename markers\n");
|
||||||
|
// first correct complex assotiations in assoc
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
size_t p = *ptr;
|
||||||
|
if(!p){continue;}
|
||||||
|
*ptr = assoc[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("time for step 3: %gs\n", dtime()-t0);
|
||||||
|
#ifdef EBUG
|
||||||
|
printf("\n\n");
|
||||||
|
for(y = 0; y < H; y++){
|
||||||
|
size_t *ptr = &labels[y*W];
|
||||||
|
for(int x = 0; x < W; x++, ptr++){
|
||||||
|
if(*ptr) printf("%02zx", *ptr);
|
||||||
|
else printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FREE(assoc);
|
||||||
|
FREE(labels);
|
||||||
75
erosion-dilation/dilation.h
Normal file
75
erosion-dilation/dilation.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* dilation.h - inner part of function `dilation`
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HERE'S NO ANY "FILE-GUARDS" BECAUSE FILE IS MULTIPLY INCLUDED!
|
||||||
|
* I use this because don't want to dive into depths of BOOST
|
||||||
|
*
|
||||||
|
* multi-including with different defines before - is a simplest way to
|
||||||
|
* modify simple code for avoiding extra if-then-else
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#if ! defined IM_UP && ! defined IM_DOWN // image without upper and lower borders
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 1; y < h; y++)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
unsigned char *iptr = &image[W*y];
|
||||||
|
unsigned char *optr = &ret[W*y];
|
||||||
|
unsigned char p = DIL[*iptr]
|
||||||
|
#ifndef IM_UP
|
||||||
|
| iptr[-W]
|
||||||
|
#endif
|
||||||
|
#ifndef IM_DOWN
|
||||||
|
| iptr[W]
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
int x;
|
||||||
|
if(iptr[1] & 0x80) p |= 1;
|
||||||
|
*optr = p;
|
||||||
|
optr++; iptr++;
|
||||||
|
for(x = 1; x < w; x++, iptr++, optr++){
|
||||||
|
p = DIL[*iptr]
|
||||||
|
#ifndef IM_UP
|
||||||
|
| iptr[-W]
|
||||||
|
#endif
|
||||||
|
#ifndef IM_DOWN
|
||||||
|
| iptr[W]
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if(iptr[1] & 0x80) p |= 1;
|
||||||
|
if(iptr[-1] & 1) p |= 0x80;
|
||||||
|
*optr = p;
|
||||||
|
}
|
||||||
|
p = DIL[*iptr]
|
||||||
|
#ifndef IM_UP
|
||||||
|
| iptr[-W]
|
||||||
|
#endif
|
||||||
|
#ifndef IM_DOWN
|
||||||
|
| iptr[W]
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if(iptr[-1] & 1) p |= 0x80;
|
||||||
|
*optr = p;
|
||||||
|
optr++; iptr++;
|
||||||
|
}
|
||||||
|
|
||||||
67
erosion-dilation/fc_filter.h
Normal file
67
erosion-dilation/fc_filter.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* fc_filter.h - inner part of function `FC_filter`
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The same shit as for dilation.h
|
||||||
|
|
||||||
|
#if ! defined IM_UP && ! defined IM_DOWN
|
||||||
|
OMP_FOR()
|
||||||
|
for(y = 1; y < h; y++)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
unsigned char *iptr = &image[W*y];
|
||||||
|
unsigned char *optr = &ret[W*y];
|
||||||
|
unsigned char p = *iptr << 1 | *iptr >> 1
|
||||||
|
#ifndef IM_UP
|
||||||
|
| iptr[-W]
|
||||||
|
#endif
|
||||||
|
#ifndef IM_DOWN
|
||||||
|
| iptr[W]
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
int x;
|
||||||
|
if(iptr[1] & 0x80) p |= 1;
|
||||||
|
*optr = p & *iptr;
|
||||||
|
optr++; iptr++;
|
||||||
|
for(x = 1; x < w; x++, iptr++, optr++){
|
||||||
|
p = *iptr << 1 | *iptr >> 1
|
||||||
|
#ifndef IM_UP
|
||||||
|
| iptr[-W]
|
||||||
|
#endif
|
||||||
|
#ifndef IM_DOWN
|
||||||
|
| iptr[W]
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if(iptr[1] & 0x80) p |= 1;
|
||||||
|
if(iptr[-1] & 1) p |= 0x80;
|
||||||
|
*optr = p & *iptr;
|
||||||
|
}
|
||||||
|
p = *iptr << 1 | *iptr >> 1
|
||||||
|
#ifndef IM_UP
|
||||||
|
| iptr[-W]
|
||||||
|
#endif
|
||||||
|
#ifndef IM_DOWN
|
||||||
|
| iptr[W]
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if(iptr[-1] & 1) p |= 0x80;
|
||||||
|
*optr = p & *iptr;
|
||||||
|
optr++; iptr++;
|
||||||
|
}
|
||||||
0
erosion-dilation/fs_filter.h
Normal file
0
erosion-dilation/fs_filter.h
Normal file
173
erosion-dilation/main.c
Normal file
173
erosion-dilation/main.c
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* main.c - test file for binmorph.c
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "binmorph.h"
|
||||||
|
|
||||||
|
// these includes are for randini
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
/*
|
||||||
|
* Generate a quasy-random number to initialize PRNG
|
||||||
|
* name: throw_random_seed
|
||||||
|
* @return value for curandSetPseudoRandomGeneratorSeed or srand48
|
||||||
|
*/
|
||||||
|
long throw_random_seed(){
|
||||||
|
//FNAME();
|
||||||
|
long r_ini;
|
||||||
|
int fail = 0;
|
||||||
|
int fd = open("/dev/random", O_RDONLY);
|
||||||
|
do{
|
||||||
|
if(-1 == fd){
|
||||||
|
fail = 1; break;
|
||||||
|
}
|
||||||
|
if(sizeof(long) != read(fd, &r_ini, sizeof(long))){
|
||||||
|
fail = 1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}while(0);
|
||||||
|
if(fail){
|
||||||
|
double tt = dtime() * 1e6;
|
||||||
|
double mx = (double)LONG_MAX;
|
||||||
|
r_ini = (long)(tt - mx * floor(tt/mx));
|
||||||
|
}
|
||||||
|
return (r_ini);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int ac, char **v){
|
||||||
|
int i,j, W = 28, H = 28, W_0;
|
||||||
|
//double midX = (W - 1.0)/ 4. - 1., midY = (H - 1.) / 4. - 1.;
|
||||||
|
//double ro = sqrt(midX*midY), ri = ro / 1.5;
|
||||||
|
bool *inp = Malloc(W * H, sizeof(bool));
|
||||||
|
printf("\n");
|
||||||
|
srand48(throw_random_seed());
|
||||||
|
for(i = 0; i < H; i++)
|
||||||
|
for(j = 0; j < W; j++)
|
||||||
|
inp[i*W+j] = (drand48() > 0.5);
|
||||||
|
/*
|
||||||
|
for(i = 0; i < H/2; i++){
|
||||||
|
for(j = 0; j < W/2; j++){
|
||||||
|
double x = j - midX, y = i - midY;
|
||||||
|
double r = sqrt(x*x + y*y);
|
||||||
|
if((r < ro && r > ri) || fabs(x) == fabs(y))
|
||||||
|
inp[i*W+j] = inp[i*W+j+W/2] = inp[(i+H/2)*W+j] = inp[(i+H/2)*W+j+W/2] = true;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
unsigned char *b2ch = bool2char(inp, W, H, &W_0);
|
||||||
|
FREE(inp);
|
||||||
|
printf("inpt: (%dx%d)\n", W_0, H);
|
||||||
|
printC(b2ch, W_0, H);
|
||||||
|
unsigned char *eros = erosion(b2ch, W_0, H);
|
||||||
|
printf("erosion: (%dx%d)\n", W_0, H);
|
||||||
|
printC(eros, W_0, H);
|
||||||
|
unsigned char *dilat = dilation(b2ch, W_0, H);
|
||||||
|
printf("dilation: (%dx%d)\n", W_0, H);
|
||||||
|
printC(dilat, W_0, H);
|
||||||
|
unsigned char *erdilat = dilation(eros, W_0, H);
|
||||||
|
printf("\n\ndilation of erosion (openinfg: (%dx%d)\n", W_0, H);
|
||||||
|
printC(erdilat, W_0, H);
|
||||||
|
unsigned char *dileros = erosion(dilat, W_0, H);
|
||||||
|
printf("erosion of dilation (closing): (%dx%d)\n", W_0, H);
|
||||||
|
printC(dileros, W_0, H);
|
||||||
|
printf("\n\n\n image - opening of original minus original:\n");
|
||||||
|
unsigned char *immer = substim(b2ch, erdilat, W_0, H);
|
||||||
|
printC(immer, W_0, H);
|
||||||
|
FREE(eros); FREE(dilat); FREE(erdilat); FREE(dileros);
|
||||||
|
inp = char2bool(immer, W, H, W_0);
|
||||||
|
printf("\n\nAnd boolean for previous image (%dx%d):\n ",W,H);
|
||||||
|
printB(inp, W, H);
|
||||||
|
FREE(immer);
|
||||||
|
immer = FC_filter(b2ch, W_0, H);
|
||||||
|
printf("\n\n\nFilter for 4-connected areas searching:\n");
|
||||||
|
printC(immer, W_0, H);
|
||||||
|
FREE(immer);
|
||||||
|
size_t NL;
|
||||||
|
printf("\nmark 8-connected components:\n");
|
||||||
|
cclabel8_1(b2ch, W, H, W_0, &NL);
|
||||||
|
printf("\nmark 8-connected components (old):\n");
|
||||||
|
cclabel8(b2ch, W, H, W_0, &NL);
|
||||||
|
FREE(b2ch);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
W = H = 1000;
|
||||||
|
FREE(inp);
|
||||||
|
inp = Malloc(W * H, sizeof(bool));
|
||||||
|
for(i = 0; i < H; i++)
|
||||||
|
for(j = 0; j < W; j++)
|
||||||
|
inp[i*W+j] = (drand48() > 0.5);
|
||||||
|
b2ch = bool2char(inp, W, H, &W_0);
|
||||||
|
FREE(inp);
|
||||||
|
printf("\n\n\n1) 1 cc4 for 2000x2000 .. ");
|
||||||
|
fflush(stdout);
|
||||||
|
double t0 = dtime();
|
||||||
|
for(i = 0; i < 1; i++){
|
||||||
|
CCbox *b = cclabel4(b2ch, W, H, W_0, &NL);
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
printf("%.3f s\n", dtime() - t0);
|
||||||
|
printf("\n2) 1 cc8 for 2000x2000 .. ");
|
||||||
|
fflush(stdout);
|
||||||
|
t0 = dtime();
|
||||||
|
for(i = 0; i < 1; i++){
|
||||||
|
CCbox *b = cclabel8(b2ch, W, H, W_0, &NL);
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
printf("%.3f s\n", dtime() - t0);
|
||||||
|
printf("\n3) 1 cc8_1 for 2000x2000 .. ");
|
||||||
|
fflush(stdout);
|
||||||
|
t0 = dtime();
|
||||||
|
for(i = 0; i < 1; i++){
|
||||||
|
CCbox *b = cclabel8_1(b2ch, W, H, W_0, &NL);
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
printf("%.3f s\n", dtime() - t0);
|
||||||
|
|
||||||
|
/* printf("\n\n\n\nSome tests:\n1) 10000 dilations for 2000x2000 .. ");
|
||||||
|
fflush(stdout);
|
||||||
|
double t0 = dtime();
|
||||||
|
for(i = 0; i < 10000; i++){
|
||||||
|
unsigned char *dilat = dilation(b2ch, W, H);
|
||||||
|
free(dilat);
|
||||||
|
}
|
||||||
|
printf("%.3f s\n", dtime() - t0);
|
||||||
|
printf("2) 10000 erosions for 2000x2000 .. ");
|
||||||
|
fflush(stdout);
|
||||||
|
t0 = dtime();
|
||||||
|
for(i = 0; i < 10000; i++){
|
||||||
|
unsigned char *dilat = erosion(b2ch, W, H);
|
||||||
|
free(dilat);
|
||||||
|
}
|
||||||
|
printf("%.3f s\n", dtime() - t0);
|
||||||
|
printf("3) 10000 image substitutions for 2000x2000 .. ");
|
||||||
|
fflush(stdout);
|
||||||
|
unsigned char *dilat = dilation(b2ch, W, H);
|
||||||
|
t0 = dtime();
|
||||||
|
for(i = 0; i < 10000; i++){
|
||||||
|
unsigned char *res = substim(b2ch, dilat, W, H);
|
||||||
|
free(res);
|
||||||
|
}
|
||||||
|
printf("%.3f s\n", dtime() - t0);*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
129
fifo_lifo.c
Normal file
129
fifo_lifo.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* fifo_lifo.c - simple FIFO/LIFO
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#include "fifo_lifo.h"
|
||||||
|
|
||||||
|
#ifndef Malloc
|
||||||
|
#define Malloc my_alloc
|
||||||
|
/**
|
||||||
|
* Memory allocation with control
|
||||||
|
* @param N - number of elements
|
||||||
|
* @param S - size of one element
|
||||||
|
* @return allocated memory or die
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef FREE
|
||||||
|
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* push data into the tail of a stack (like FIFO)
|
||||||
|
* @param lst (io) - list
|
||||||
|
* @param v (i) - data to push
|
||||||
|
* @return pointer to just pused node
|
||||||
|
*/
|
||||||
|
List *push_tail(List **lst, Ldata v){
|
||||||
|
List *node;
|
||||||
|
if(!lst) return NULL;
|
||||||
|
if((node = (List*) Malloc(1, sizeof(List))) == 0)
|
||||||
|
return NULL; // allocation error
|
||||||
|
node->data = v; // insert data
|
||||||
|
if(!*lst){
|
||||||
|
*lst = node;
|
||||||
|
(*lst)->last = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
(*lst)->last->next = node;
|
||||||
|
(*lst)->last = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* push data into the head of a stack (like LIFO)
|
||||||
|
* @param lst (io) - list
|
||||||
|
* @param v (i) - data to push
|
||||||
|
* @return pointer to just pused node
|
||||||
|
*/
|
||||||
|
List *push(List **lst, Ldata v){
|
||||||
|
List *node;
|
||||||
|
if(!lst) return NULL;
|
||||||
|
if((node = (List*) Malloc(1, sizeof(List))) == 0)
|
||||||
|
return NULL; // allocation error
|
||||||
|
node->data = v; // insert data
|
||||||
|
if(!*lst){
|
||||||
|
*lst = node;
|
||||||
|
(*lst)->last = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node->next = *lst;
|
||||||
|
node->last = (*lst)->last;
|
||||||
|
*lst = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get data from head of list
|
||||||
|
* @param lst (io) - list
|
||||||
|
* @return data from lst head
|
||||||
|
*/
|
||||||
|
Ldata pop(List **lst){
|
||||||
|
Ldata ret;
|
||||||
|
List *node = *lst;
|
||||||
|
if(!lst) errx(1, "NULL pointer to buffer");
|
||||||
|
if(!*lst) errx(1, "Empty buffer");
|
||||||
|
ret = (*lst)->data;
|
||||||
|
*lst = (*lst)->next;
|
||||||
|
FREE(node);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
int main(void) {
|
||||||
|
List *f = NULL;
|
||||||
|
int i, d, ins[] = {4,2,6,1,3,4,7};
|
||||||
|
for(i = 0; i < 7; i++)
|
||||||
|
if(!(push(&f, ins[i])))
|
||||||
|
err(1, "Malloc error"); // can't insert
|
||||||
|
else printf("pushed %d\n", ins[i]);
|
||||||
|
while(f){
|
||||||
|
d = pop(&f);
|
||||||
|
printf("pulled: %d\n", d);
|
||||||
|
}
|
||||||
|
for(i = 0; i < 7; i++)
|
||||||
|
if(!(push_tail(&f, ins[i])))
|
||||||
|
err(1, "Malloc error"); // can't insert
|
||||||
|
else printf("pushed to head %d\n", ins[i]);
|
||||||
|
while(f){
|
||||||
|
d = pop(&f);
|
||||||
|
printf("pulled: %d\n", d);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
37
fifo_lifo.h
Normal file
37
fifo_lifo.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* fifo_lifo.h
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __FIFO_LIFO_H__
|
||||||
|
#define __FIFO_LIFO_H__
|
||||||
|
|
||||||
|
typedef int Ldata;
|
||||||
|
|
||||||
|
typedef struct buff_node{
|
||||||
|
Ldata data;
|
||||||
|
struct buff_node *next, *last;
|
||||||
|
} List;
|
||||||
|
|
||||||
|
List *push_tail(List **lst, Ldata v);
|
||||||
|
List *push(List **lst, Ldata v);
|
||||||
|
Ldata pop(List **lst);
|
||||||
|
|
||||||
|
#endif // __FIFO_LIFO_H__
|
||||||
9
getopt/README
Normal file
9
getopt/README
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
1. Functions for simplify control of complex parameters
|
||||||
|
cmdlnopts.c - example of use
|
||||||
|
parceargs.c - main functions
|
||||||
|
parceargs.h - their header
|
||||||
|
|
||||||
|
2. Functions for regular getopt_long
|
||||||
|
getopt.c - snippet file
|
||||||
|
getopt.h - its header
|
||||||
|
|
||||||
123
getopt/cmdlnopts.c
Normal file
123
getopt/cmdlnopts.c
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* cmdlnopts.c - the only function that parce cmdln args and returns glob parameters
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are global parameters initialisation
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define command line options by filling structure:
|
||||||
|
* name has_arg flag val type argptr help
|
||||||
|
*/
|
||||||
|
myoption cmdlnopts[] = {
|
||||||
|
{"help", 0, NULL, 'h', arg_int, APTR(&help), N_("show this help")},
|
||||||
|
// ...
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse string of suboptions (--option=name1=var1:name2=var2... or -O name1=var1,name2=var2...)
|
||||||
|
* Suboptions could be divided by colon or comma
|
||||||
|
*
|
||||||
|
* !!NAMES OF SUBOPTIONS ARE CASE-UNSENSITIVE!!!
|
||||||
|
*
|
||||||
|
* @param arg (i) - string with description
|
||||||
|
* @param V (io) - pointer to suboptions array (result will be stored in sopts->val)
|
||||||
|
* @return TRUE if success
|
||||||
|
*/
|
||||||
|
bool get_suboptions(void *arg, suboptions *V){
|
||||||
|
char *tok, *val, *par;
|
||||||
|
int i;
|
||||||
|
tok = strtok(arg, ":,");
|
||||||
|
do{
|
||||||
|
if((val = strchr(tok, '=')) == NULL){ // wrong format
|
||||||
|
WARNX(_("Wrong format: no value for keyword"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*val++ = '\0';
|
||||||
|
par = tok;
|
||||||
|
for(i = 0; V[i].val; i++){
|
||||||
|
if(strcasecmp(par, V[i].par) == 0){ // found parameter
|
||||||
|
if(V[i].isdegr){ // DMS
|
||||||
|
if(!get_radians(V[i].val, val)) // wrong angle
|
||||||
|
return FALSE;
|
||||||
|
DBG("Angle: %g rad\n", *(V[i].val));
|
||||||
|
}else{ // simple float
|
||||||
|
if(!myatof(V[i].val, val)) // wrong number
|
||||||
|
return FALSE;
|
||||||
|
DBG("Float val: %g\n", *(V[i].val));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!V[i].val){ // nothing found - wrong format
|
||||||
|
WARNX(_("Bad keyword!"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}while((tok = strtok(NULL, ":,")));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions of subargs parcing can looks as this
|
||||||
|
*/
|
||||||
|
bool get_mir_par(void *arg, int N _U_){
|
||||||
|
suboptions V[] = { // array of mirror parameters and string keys for cmdln pars
|
||||||
|
{&M.D, "diam", FALSE},
|
||||||
|
{&M.F, "foc", FALSE},
|
||||||
|
{&M.Zincl, "zincl", TRUE},
|
||||||
|
{&M.Aincl, "aincl", TRUE},
|
||||||
|
{&M.objA, "aobj", TRUE},
|
||||||
|
{&M.objZ, "zobj", TRUE},
|
||||||
|
{&M.foc, "ccd", FALSE},
|
||||||
|
{0,0,0}
|
||||||
|
};
|
||||||
|
return get_suboptions(arg, V);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parce command line options and return dynamically allocated structure
|
||||||
|
* to global parameters
|
||||||
|
* @param argc - copy of argc from main
|
||||||
|
* @param argv - copy of argv from main
|
||||||
|
* @return allocated structure with global parameters
|
||||||
|
*/
|
||||||
|
glob_pars *parce_args(int argc, char **argv){
|
||||||
|
int i;
|
||||||
|
void *ptr;
|
||||||
|
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||||
|
ptr = memcpy(&M, &Mdefault, sizeof(M)); assert(ptr);
|
||||||
|
G.Mirror = &M;
|
||||||
|
// format of help: "Usage: progname [args]\n"
|
||||||
|
change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n");
|
||||||
|
// parse arguments
|
||||||
|
parceargs(&argc, &argv, cmdlnopts);
|
||||||
|
if(help) showhelp(-1, cmdlnopts);
|
||||||
|
if(argc > 0){
|
||||||
|
printf("\nIgnore argument[s]:\n");
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
printf("\t%s\n", argv[i]);
|
||||||
|
}
|
||||||
|
return &G;
|
||||||
|
}
|
||||||
|
|
||||||
36
getopt/cmdlnopts.h
Normal file
36
getopt/cmdlnopts.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* cmdlnopts.h - comand line options for parceargs
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __CMDLNOPTS_H__
|
||||||
|
#define __CMDLNOPTS_H__
|
||||||
|
|
||||||
|
#include "parceargs.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are some typedef's for global data
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef SomeType glob_pars;
|
||||||
|
|
||||||
|
glob_pars *parce_args(int argc, char **argv);
|
||||||
|
|
||||||
|
#endif // __CMDLNOPTS_H__
|
||||||
128
getopt/getopt.c
Normal file
128
getopt/getopt.c
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* getopt.c - simple functions for getopt_long
|
||||||
|
* provide functions
|
||||||
|
* usage - to show help message & exit
|
||||||
|
* parse_args - to parce argv & fill global flags
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are global variables and global data structures initialisation, like
|
||||||
|
* int val = 10; // default value
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HERE A PART of main.c:
|
||||||
|
*
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
textdomain(GETTEXT_PACKAGE);
|
||||||
|
parse_args(argc, argv); // <- set all flags
|
||||||
|
*
|
||||||
|
* ITS HEADER:
|
||||||
|
#ifndef GETTEXT_PACKAGE // should be defined by make
|
||||||
|
#define GETTEXT_PACKAGE "progname"
|
||||||
|
#endif
|
||||||
|
#ifndef LOCALEDIR // should be defined by make
|
||||||
|
#define LOCALEDIR "/path/to/locale"
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
void usage(char *fmt, ...){
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
printf("\n");
|
||||||
|
if (fmt != NULL){
|
||||||
|
vprintf(fmt, ap);
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
// "éÓÐÏÌØÚÏ×ÁÎÉÅ:\t%s [ÏÐÃÉÉ] [ÐÒÅÆÉËÓ ×ÙÈÏÄÎÙÈ ÆÁÊÌÏ×]\n"
|
||||||
|
printf(_("Usage:\t%s [options] [output files prefix]\n"),
|
||||||
|
__progname);
|
||||||
|
// "\tïÐÃÉÉ:\n"
|
||||||
|
printf(_("\tOptions:\n"));
|
||||||
|
printf("\t-A,\t--author=author\t\t%s\n",
|
||||||
|
// "Á×ÔÏÒ ÐÒÏÇÒÁÍÍÙ"
|
||||||
|
_("program author"));
|
||||||
|
// and so on
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_args(int argc, char **argv){
|
||||||
|
int i;
|
||||||
|
char short_options[] = "A"; // all short equivalents
|
||||||
|
struct option long_options[] = {
|
||||||
|
/* { name, has_arg, flag, val }, where:
|
||||||
|
* name - name of long parameter
|
||||||
|
* has_arg = 0 - no argument, 1 - need argument, 2 - unnesessary argument
|
||||||
|
* flag = NULL to return val, pointer to int - to set it
|
||||||
|
* value of val (function returns 0)
|
||||||
|
* val - getopt_long return value or value, flag setting to
|
||||||
|
* !!! last string - for zeros !!!
|
||||||
|
*/
|
||||||
|
{"author", 1, 0, 'A'},
|
||||||
|
// and so on
|
||||||
|
{ 0, 0, 0, 0 }
|
||||||
|
};
|
||||||
|
if(argc == 1){
|
||||||
|
// "îÅ ××ÅÄÅÎÏ ÎÉËÁËÉÈ ÐÁÒÁÍÅÔÒÏ×"
|
||||||
|
usage(_("Any parameters are absent"));
|
||||||
|
}
|
||||||
|
while(1){
|
||||||
|
int opt;
|
||||||
|
if((opt = getopt_long(argc, argv, short_options,
|
||||||
|
long_options, NULL)) == -1) break;
|
||||||
|
switch(opt){
|
||||||
|
case 0: // only long option
|
||||||
|
// do something?
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
author = strdup(optarg);
|
||||||
|
// "á×ÔÏÒ ÐÒÏÇÒÁÍÍÙ: %s"
|
||||||
|
//info(_("Program author: %s"), author);
|
||||||
|
break;
|
||||||
|
// ...
|
||||||
|
default:
|
||||||
|
usage(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if(argc == 0){
|
||||||
|
// there's no free parameters
|
||||||
|
}else{
|
||||||
|
/* there was free parameter
|
||||||
|
* for example, filename
|
||||||
|
outfile = argv[0];
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
if(argc > 0){
|
||||||
|
// "éÇÎÏÒÉÒÕÀ ÁÒÇÕÍÅÎÔ[Ù]:\n"
|
||||||
|
printf(_("Ignore argument[s]:\n"));
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
warnx("%s ", argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
getopt/getopt.h
Normal file
18
getopt/getopt.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __USAGE_H__
|
||||||
|
#define __USAGE_H__
|
||||||
|
|
||||||
|
#include "takepic.h"
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are global variables and global data structures definition, like
|
||||||
|
* int val;
|
||||||
|
* enum{reset = 0, set};
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern char *__progname;
|
||||||
|
void usage(char *fmt, ...);
|
||||||
|
void parse_args(int argc, char **argv);
|
||||||
|
|
||||||
|
#endif // __USAGE_H__
|
||||||
316
getopt/parceargs.c
Normal file
316
getopt/parceargs.c
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* parceargs.c - parcing command line arguments & print help
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h> // DBG
|
||||||
|
#include <getopt.h> // getopt_long
|
||||||
|
#include <stdlib.h> // calloc, exit, strtoll
|
||||||
|
#include <assert.h> // assert
|
||||||
|
#include <string.h> // strdup, strchr, strlen
|
||||||
|
#include <limits.h> // INT_MAX & so on
|
||||||
|
#include <libintl.h>// gettext
|
||||||
|
#include <ctype.h> // isalpha
|
||||||
|
#include "parceargs.h"
|
||||||
|
|
||||||
|
#ifdef EBUG
|
||||||
|
#define DBG(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DBG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macro to print help messages
|
||||||
|
#ifndef PRNT
|
||||||
|
#define PRNT(x) gettext(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *helpstring = "%s\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change standard help header
|
||||||
|
* MAY consist ONE "%s" for progname
|
||||||
|
* @param str (i) - new format
|
||||||
|
*/
|
||||||
|
void change_helpstring(char *s){
|
||||||
|
int pcount = 0, scount = 0;
|
||||||
|
char *str = s;
|
||||||
|
// check `helpstring` and set it to default in case of error
|
||||||
|
for(; pcount < 2; str += 2){
|
||||||
|
if(!(str = strchr(str, '%'))) break;
|
||||||
|
if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%"
|
||||||
|
else{
|
||||||
|
str += 2; // pass next '%'
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(str[1] == 's') scount++; // increment "%s" counter
|
||||||
|
};
|
||||||
|
DBG("pc: %d, sc: %d\n", pcount, scount);
|
||||||
|
if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong
|
||||||
|
fprintf(stderr, "Wrong helpstring!\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
helpstring = s;
|
||||||
|
DBG("hs: %s\n", helpstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carefull atoll/atoi
|
||||||
|
* @param num (o) - returning value (or NULL if you wish only check number) - allocated by user
|
||||||
|
* @param str (i) - string with number must not be NULL
|
||||||
|
* @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more)
|
||||||
|
* @return TRUE if conversion sone without errors, FALSE otherwise
|
||||||
|
*/
|
||||||
|
bool myatoll(void *num, char *str, argtype t){
|
||||||
|
long long tmp, *llptr;
|
||||||
|
int *iptr;
|
||||||
|
char *endptr;
|
||||||
|
assert(str);
|
||||||
|
assert(num);
|
||||||
|
tmp = strtoll(str, &endptr, 0);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return FALSE;
|
||||||
|
switch(t){
|
||||||
|
case arg_longlong:
|
||||||
|
llptr = (long long*) num;
|
||||||
|
*llptr = tmp;
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
default:
|
||||||
|
if(tmp < INT_MIN || tmp > INT_MAX){
|
||||||
|
fprintf(stderr, "Integer out of range\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
iptr = (int*)num;
|
||||||
|
*iptr = (int)tmp;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same as myatoll but for double
|
||||||
|
// There's no NAN & INF checking here (what if they would be needed?)
|
||||||
|
bool myatod(void *num, const char *str, argtype t){
|
||||||
|
double tmp, *dptr;
|
||||||
|
float *fptr;
|
||||||
|
char *endptr;
|
||||||
|
assert(str);
|
||||||
|
tmp = strtod(str, &endptr);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return FALSE;
|
||||||
|
switch(t){
|
||||||
|
case arg_double:
|
||||||
|
dptr = (double *) num;
|
||||||
|
*dptr = tmp;
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
default:
|
||||||
|
fptr = (float *) num;
|
||||||
|
*fptr = (float)tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get index of current option in array options
|
||||||
|
* @param opt (i) - returning val of getopt_long
|
||||||
|
* @param options (i) - array of options
|
||||||
|
* @return index in array
|
||||||
|
*/
|
||||||
|
int get_optind(int opt, myoption *options){
|
||||||
|
int oind;
|
||||||
|
myoption *opts = options;
|
||||||
|
assert(opts);
|
||||||
|
for(oind = 0; opts->name && opts->val != opt; oind++, opts++);
|
||||||
|
if(!opts->name || opts->val != opt) // no such parameter
|
||||||
|
showhelp(-1, options);
|
||||||
|
return oind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parce command line arguments
|
||||||
|
* ! If arg is string, then value will be strdup'ed!
|
||||||
|
*
|
||||||
|
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
|
||||||
|
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt`
|
||||||
|
* BE CAREFUL! if you wanna use full argc & argv, save their original values before
|
||||||
|
* calling this function
|
||||||
|
* @param options (i) - array of `myoption` for arguments parcing
|
||||||
|
*
|
||||||
|
* @exit: in case of error this function show help & make `exit(-1)`
|
||||||
|
*/
|
||||||
|
void parceargs(int *argc, char ***argv, myoption *options){
|
||||||
|
char *short_options, *soptr;
|
||||||
|
struct option *long_options, *loptr;
|
||||||
|
size_t optsize, i;
|
||||||
|
myoption *opts = options;
|
||||||
|
// check whether there is at least one options
|
||||||
|
assert(opts);
|
||||||
|
assert(opts[0].name);
|
||||||
|
// first we count how much values are in opts
|
||||||
|
for(optsize = 0; opts->name; optsize++, opts++);
|
||||||
|
// now we can allocate memory
|
||||||
|
short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts
|
||||||
|
long_options = calloc(optsize + 1, sizeof(struct option));
|
||||||
|
opts = options; loptr = long_options; soptr = short_options;
|
||||||
|
// fill short/long parameters and make a simple checking
|
||||||
|
for(i = 0; i < optsize; i++, loptr++, opts++){
|
||||||
|
// check
|
||||||
|
assert(opts->name); // check name
|
||||||
|
if(opts->has_arg){
|
||||||
|
assert(opts->type != arg_none); // check error with arg type
|
||||||
|
assert(opts->argptr); // check pointer
|
||||||
|
}
|
||||||
|
if(opts->type != arg_none) // if there is a flag without arg, check its pointer
|
||||||
|
assert(opts->argptr);
|
||||||
|
// fill long_options
|
||||||
|
// don't do memcmp: what if there would be different alignment?
|
||||||
|
loptr->name = opts->name;
|
||||||
|
loptr->has_arg = opts->has_arg;
|
||||||
|
loptr->flag = opts->flag;
|
||||||
|
loptr->val = opts->val;
|
||||||
|
// fill short options if they are:
|
||||||
|
if(!opts->flag){
|
||||||
|
*soptr++ = opts->val;
|
||||||
|
if(opts->has_arg) // add ':' if option has required argument
|
||||||
|
*soptr++ = ':';
|
||||||
|
if(opts->has_arg == 2) // add '::' if option has optional argument
|
||||||
|
*soptr++ = ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now we have both long_options & short_options and can parse `getopt_long`
|
||||||
|
while(1){
|
||||||
|
int opt;
|
||||||
|
int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[]
|
||||||
|
if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break;
|
||||||
|
if(opt == '?'){
|
||||||
|
opt = optopt;
|
||||||
|
optind = get_optind(opt, options);
|
||||||
|
if(options[optind].has_arg == 1) showhelp(optind, options); // need argument
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(opt == 0 || oindex > 0) optind = oindex;
|
||||||
|
else optind = get_optind(opt, options);
|
||||||
|
}
|
||||||
|
opts = &options[optind];
|
||||||
|
DBG ("\n*******\noption %s (oindex = %d / optind = %d)", options[optind].name, oindex, optind);
|
||||||
|
if(optarg) DBG (" with arg %s", optarg);
|
||||||
|
DBG ("\n");
|
||||||
|
if(opt == 0 && opts->has_arg == 0) continue; // only long option changing integer flag
|
||||||
|
DBG("opt = %c, arg type: ", opt);
|
||||||
|
// now check option
|
||||||
|
if(opts->has_arg == 1) assert(optarg);
|
||||||
|
bool result = TRUE;
|
||||||
|
// even if there is no argument, but argptr != NULL, think that optarg = "1"
|
||||||
|
if(!optarg) optarg = "1";
|
||||||
|
switch(opts->type){
|
||||||
|
default:
|
||||||
|
case arg_none:
|
||||||
|
DBG("none\n");
|
||||||
|
if(opts->argptr) *((int*)opts->argptr) = 1; // set argptr to 1
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
DBG("integer\n");
|
||||||
|
result = myatoll(opts->argptr, optarg, arg_int);
|
||||||
|
break;
|
||||||
|
case arg_longlong:
|
||||||
|
DBG("long long\n");
|
||||||
|
result = myatoll(opts->argptr, optarg, arg_longlong);
|
||||||
|
break;
|
||||||
|
case arg_double:
|
||||||
|
DBG("double\n");
|
||||||
|
result = myatod(opts->argptr, optarg, arg_double);
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
DBG("double\n");
|
||||||
|
result = myatod(opts->argptr, optarg, arg_float);
|
||||||
|
break;
|
||||||
|
case arg_string:
|
||||||
|
DBG("string\n");
|
||||||
|
result = (*((char **)opts->argptr) = strdup(optarg));
|
||||||
|
break;
|
||||||
|
case arg_function:
|
||||||
|
DBG("function\n");
|
||||||
|
result = ((argfn)opts->argptr)(optarg, optind);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!result){
|
||||||
|
DBG("OOOPS! Error in result\n");
|
||||||
|
showhelp(optind, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*argc -= optind;
|
||||||
|
*argv += optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show help information based on myoption->help values
|
||||||
|
* @param oindex (i) - if non-negative, show only help by myoption[oindex].help
|
||||||
|
* @param options (i) - array of `myoption`
|
||||||
|
*
|
||||||
|
* @exit: run `exit(-1)` !!!
|
||||||
|
*/
|
||||||
|
void showhelp(int oindex, myoption *options){
|
||||||
|
// ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
|
||||||
|
// but you can redefine it before `#include "parceargs.h"`
|
||||||
|
int max_opt_len = 0; // max len of options substring - for right indentation
|
||||||
|
const int bufsz = 255;
|
||||||
|
char buf[bufsz+1];
|
||||||
|
myoption *opts = options;
|
||||||
|
assert(opts);
|
||||||
|
assert(opts[0].name); // check whether there is at least one options
|
||||||
|
if(oindex > -1){ // print only one message
|
||||||
|
opts = &options[oindex];
|
||||||
|
printf(" ");
|
||||||
|
if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val);
|
||||||
|
printf("--%s", opts->name);
|
||||||
|
if(opts->has_arg == 1) printf("=arg");
|
||||||
|
else if(opts->has_arg == 2) printf("[=arg]");
|
||||||
|
printf(" %s\n", PRNT(opts->help));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// header, by default is just "progname\n"
|
||||||
|
printf("\n");
|
||||||
|
if(strstr(helpstring, "%s")) // print progname
|
||||||
|
printf(helpstring, __progname);
|
||||||
|
else // only text
|
||||||
|
printf("%s", helpstring);
|
||||||
|
printf("\n");
|
||||||
|
// count max_opt_len
|
||||||
|
do{
|
||||||
|
int L = strlen(opts->name);
|
||||||
|
if(max_opt_len < L) max_opt_len = L;
|
||||||
|
}while((++opts)->name);
|
||||||
|
max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols
|
||||||
|
opts = options;
|
||||||
|
// Now print all help
|
||||||
|
do{
|
||||||
|
int p = sprintf(buf, " "); // a little indent
|
||||||
|
if(!opts->flag && isalpha(opts->val)) // .val is short argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val);
|
||||||
|
p += snprintf(buf+p, bufsz-p, "--%s", opts->name);
|
||||||
|
if(opts->has_arg == 1) // required argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "=arg");
|
||||||
|
else if(opts->has_arg == 2) // optional argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "[=arg]");
|
||||||
|
assert(p < max_opt_len); // there would be magic if p >= max_opt_len
|
||||||
|
printf("%-*s%s\n", max_opt_len+1, buf, PRNT(opts->help)); // write options & at least 2 spaces after
|
||||||
|
}while((++opts)->name);
|
||||||
|
printf("\n\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
105
getopt/parceargs.h
Normal file
105
getopt/parceargs.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* parceargs.h - headers for parcing command line arguments
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __PARCEARGS_H__
|
||||||
|
#define __PARCEARGS_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>// bool
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macro for argptr
|
||||||
|
#define APTR(x) ((void*)x)
|
||||||
|
|
||||||
|
// if argptr is a function:
|
||||||
|
typedef bool(*argfn)(void *arg, int N);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* type of getopt's argument
|
||||||
|
* WARNING!
|
||||||
|
* My function change value of flags by pointer, so if you want to use another type
|
||||||
|
* make a latter conversion, example:
|
||||||
|
* char charg;
|
||||||
|
* int iarg;
|
||||||
|
* myoption opts[] = {
|
||||||
|
* {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option};
|
||||||
|
* ..(parce args)..
|
||||||
|
* charg = (char) iarg;
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
arg_none = 0, // no arg
|
||||||
|
arg_int, // integer
|
||||||
|
arg_longlong, // long long
|
||||||
|
arg_double, // double
|
||||||
|
arg_float, // float
|
||||||
|
arg_string, // char *
|
||||||
|
arg_function // parce_args will run function `bool (*fn)(char *optarg, int N)`
|
||||||
|
} argtype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure for getopt_long & help
|
||||||
|
* BE CAREFUL: .argptr is pointer to data or pointer to function,
|
||||||
|
* conversion depends on .type
|
||||||
|
*
|
||||||
|
* ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
|
||||||
|
* but you can redefine it before `#include "parceargs.h"`
|
||||||
|
*
|
||||||
|
* if arg is string, then value wil be strdup'ed like that:
|
||||||
|
* char *str;
|
||||||
|
* myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option};
|
||||||
|
* *(opts[1].str) = strdup(optarg);
|
||||||
|
* in other cases argptr should be address of some variable (or pointer to allocated memory)
|
||||||
|
*
|
||||||
|
* NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr
|
||||||
|
*
|
||||||
|
* !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct{
|
||||||
|
// these are from struct option:
|
||||||
|
const char *name; // long option's name
|
||||||
|
int has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg
|
||||||
|
int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0)
|
||||||
|
int val; // short opt name (if flag == NULL) or flag's value
|
||||||
|
// and these are mine:
|
||||||
|
argtype type; // type of argument
|
||||||
|
void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)`
|
||||||
|
char *help; // help string which would be shown in function `showhelp` or NULL
|
||||||
|
} myoption;
|
||||||
|
|
||||||
|
// last string of array (all zeros)
|
||||||
|
#define end_option {0,0,0,0,0,0,0}
|
||||||
|
|
||||||
|
|
||||||
|
extern const char *__progname;
|
||||||
|
|
||||||
|
void showhelp(int oindex, myoption *options);
|
||||||
|
void parceargs(int *argc, char ***argv, myoption *options);
|
||||||
|
void change_helpstring(char *s);
|
||||||
|
|
||||||
|
#endif // __PARCEARGS_H__
|
||||||
23
kicad-copy/Makefile
Normal file
23
kicad-copy/Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
PROGRAM = copy_pcb
|
||||||
|
LDFLAGS =
|
||||||
|
SRCS = cmdlnopts.c copy_pcb.c parceargs.c usefull_macros.c
|
||||||
|
CC = gcc
|
||||||
|
DEFINES = -D_XOPEN_SOURCE=1001 -DGETTEXT_PACKAGE=\"copy_pcb\" -DLOCALEDIR=\".\"
|
||||||
|
DEFINES +=-DEBUG
|
||||||
|
CXX = gcc
|
||||||
|
CFLAGS = -std=gnu99 -Wall -Werror $(DEFINES)
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
all : $(PROGRAM)
|
||||||
|
$(PROGRAM) : $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
# some addition dependencies
|
||||||
|
# %.o: %.c
|
||||||
|
# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@
|
||||||
|
#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS)
|
||||||
|
# @touch $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(SRCS)
|
||||||
65
kicad-copy/cmdlnopts.c
Normal file
65
kicad-copy/cmdlnopts.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* cmdlnopts.c - the only function that parce cmdln args and returns glob parameters
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include "copy_pcb.h"
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are global parameters initialisation
|
||||||
|
*/
|
||||||
|
glob_pars G;
|
||||||
|
int help = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define command line options by filling structure:
|
||||||
|
* name has_arg flag val type argptr help
|
||||||
|
*/
|
||||||
|
myoption cmdlnopts[] = {
|
||||||
|
{"help", 0, NULL, 'h', arg_int, APTR(&help), N_("show this help")},
|
||||||
|
{"format", 1, NULL, 'f', arg_string, APTR(&G.format), N_("printf-like format of similar items (%s - MUST BE - old name; %n - new num)")},
|
||||||
|
{"nparts", 1, NULL, 'N', arg_int, APTR(&G.nparts), N_("Number of copies")},
|
||||||
|
{"list", 1, NULL, 'L', arg_string, APTR(&G.list), N_("comma-separated list of sheets")},
|
||||||
|
{"input", 1, NULL, 'i', arg_string, APTR(&G.ifile), N_("input file name")},
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parce command line options and return dynamically allocated structure
|
||||||
|
* to global parameters
|
||||||
|
* @param argc - copy of argc from main
|
||||||
|
* @param argv - copy of argv from main
|
||||||
|
* @return allocated structure with global parameters
|
||||||
|
*/
|
||||||
|
glob_pars *parce_args(int argc, char **argv){
|
||||||
|
int i;
|
||||||
|
memset(&G, sizeof(glob_pars), 0); // clear all
|
||||||
|
// format of help: "Usage: progname [args]\n"
|
||||||
|
change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n");
|
||||||
|
// parse arguments
|
||||||
|
parceargs(&argc, &argv, cmdlnopts);
|
||||||
|
if(help) showhelp(-1, cmdlnopts);
|
||||||
|
if(argc > 0){
|
||||||
|
printf("\nIgnore argument[s]:\n");
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
printf("\t%s\n", argv[i]);
|
||||||
|
}
|
||||||
|
return &G;
|
||||||
|
}
|
||||||
|
|
||||||
43
kicad-copy/cmdlnopts.h
Normal file
43
kicad-copy/cmdlnopts.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* cmdlnopts.h - comand line options for parceargs
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __CMDLNOPTS_H__
|
||||||
|
#define __CMDLNOPTS_H__
|
||||||
|
|
||||||
|
#include "parceargs.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are some typedef's for global data
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
char *format; // printf-like format: "%s_%d"
|
||||||
|
int nparts; // number of parts from list
|
||||||
|
char *list; // comma-separated list
|
||||||
|
char *ifile; // input file
|
||||||
|
}glob_pars;
|
||||||
|
|
||||||
|
extern glob_pars G;
|
||||||
|
|
||||||
|
glob_pars *parce_args(int argc, char **argv);
|
||||||
|
|
||||||
|
#endif // __CMDLNOPTS_H__
|
||||||
115
kicad-copy/copy_pcb.c
Normal file
115
kicad-copy/copy_pcb.c
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* copy_pcb.c - copy kicad PCBs
|
||||||
|
*
|
||||||
|
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "copy_pcb.h"
|
||||||
|
|
||||||
|
char **List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Printout string from ptr0 (including) to ptr1 (excluding) and return ptr1
|
||||||
|
*/
|
||||||
|
char *printdata(char *ptr0, char *ptr1){
|
||||||
|
char *ptr = ptr0;
|
||||||
|
while(ptr < ptr1) printf("%c", *ptr++);
|
||||||
|
return ptr1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int build_list(){
|
||||||
|
char *tok;
|
||||||
|
int L, i;
|
||||||
|
tok = G.list;
|
||||||
|
for(L = 0; strchr(tok, ','); L++, tok++);
|
||||||
|
DBG("found: %d commas\n", L);
|
||||||
|
if(G.nparts > 0 && G.nparts < L) L = G.nparts;
|
||||||
|
List = MALLOC(char*, L);
|
||||||
|
tok = strtok(G.list, ",");
|
||||||
|
if(!tok) return 0;
|
||||||
|
i = 0;
|
||||||
|
do{
|
||||||
|
List[i++] = strdup(tok);
|
||||||
|
}while((tok = strtok(NULL, ",")) && i < L);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
int i, j, L;
|
||||||
|
mmapbuf *input;
|
||||||
|
initial_setup();
|
||||||
|
parce_args(argc, argv);
|
||||||
|
if(!G.ifile){
|
||||||
|
ERRX("You shold give input file name\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
if(G.format) DBG("Format: %s\n", G.format);
|
||||||
|
else{
|
||||||
|
DBG("No format defined, use \"%%s.%%d\"\n");
|
||||||
|
G.format = "%s.%d";
|
||||||
|
}
|
||||||
|
if(G.nparts) DBG("Use first %d copies of list\n", G.nparts);
|
||||||
|
else DBG("Use all list given\n");
|
||||||
|
if(!G.list){
|
||||||
|
WARNX("Error! You should say\n\t\t%s -L list\n", __progname);
|
||||||
|
ERR("\twhere \"list\" is a comma-separated list of sheets\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
L = build_list();
|
||||||
|
DBG("Found %d sheets in list:\n", L);
|
||||||
|
if(L == 0){
|
||||||
|
ERRX("Error: empty list\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
for(i = 0; i < L; i++) DBG("\titem %d: %s\n", i, List[i]);
|
||||||
|
if(!(input = My_mmap(G.ifile))){
|
||||||
|
ERRX("A strange error: can't mmap input file\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
char *iptr = input->data, *oldptr = iptr;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
char comp[32], val[32];
|
||||||
|
int N1, N2;
|
||||||
|
while((iptr = strstr(iptr, "$Comp\n"))){
|
||||||
|
DBG("%dth component: ", ++i);
|
||||||
|
iptr = strstr(iptr, "\nL ");
|
||||||
|
if(!iptr) break; iptr += 3;
|
||||||
|
if(sscanf(iptr, "%s %s\n", comp, val) != 2) continue;
|
||||||
|
DBG("component %s with label %s\n", comp, val);
|
||||||
|
iptr = strstr(iptr, "\nU ");
|
||||||
|
if(!iptr) break; iptr += 3;
|
||||||
|
if(sscanf(iptr, "%d %d %s\n",&N1,&N2,comp) != 3) continue;
|
||||||
|
DBG("N1 = %d; N2 = %d; comp label: %s\n",N1,N2,comp);
|
||||||
|
iptr = strstr(iptr, "\nF"); // go to line "F 0"
|
||||||
|
if(!iptr) break; iptr++;
|
||||||
|
// printout all what was before:
|
||||||
|
oldptr = printdata(oldptr, iptr);
|
||||||
|
for(j = 0; j < L; j++){
|
||||||
|
printf("AR Path=\"/%s/%s\" Ref=\"", List[j], comp);
|
||||||
|
printf(G.format, val, j+1);
|
||||||
|
printf("\" Part=\"1\"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// printout the rest of file
|
||||||
|
if(!iptr) iptr = input->data + input->len;
|
||||||
|
printdata(oldptr, iptr);
|
||||||
|
My_munmap(input);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
31
kicad-copy/copy_pcb.h
Normal file
31
kicad-copy/copy_pcb.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* copy_pcb.h
|
||||||
|
*
|
||||||
|
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __COPY_PCB_H__
|
||||||
|
#define __COPY_PCB_H__
|
||||||
|
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
|
||||||
|
#endif // __COPY_PCB_H__
|
||||||
|
|
||||||
|
|
||||||
315
kicad-copy/parceargs.c
Normal file
315
kicad-copy/parceargs.c
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* parceargs.c - parcing command line arguments & print help
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h> // DBG
|
||||||
|
#include <getopt.h> // getopt_long
|
||||||
|
#include <stdlib.h> // calloc, exit, strtoll
|
||||||
|
#include <assert.h> // assert
|
||||||
|
#include <string.h> // strdup, strchr, strlen
|
||||||
|
#include <limits.h> // INT_MAX & so on
|
||||||
|
#include <libintl.h>// gettext
|
||||||
|
#include <ctype.h> // isalpha
|
||||||
|
#include "parceargs.h"
|
||||||
|
|
||||||
|
#ifdef EBUG
|
||||||
|
#define DBG(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DBG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macro to print help messages
|
||||||
|
#ifndef PRNT
|
||||||
|
#define PRNT(x) gettext(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *helpstring = "%s\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change standard help header
|
||||||
|
* MAY consist ONE "%s" for progname
|
||||||
|
* @param str (i) - new format
|
||||||
|
*/
|
||||||
|
void change_helpstring(char *s){
|
||||||
|
int pcount = 0, scount = 0;
|
||||||
|
char *str = s;
|
||||||
|
// check `helpstring` and set it to default in case of error
|
||||||
|
for(; pcount < 2; str += 2){
|
||||||
|
if(!(str = strchr(str, '%'))) break;
|
||||||
|
if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%"
|
||||||
|
else{
|
||||||
|
str += 2; // pass next '%'
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(str[1] == 's') scount++; // increment "%s" counter
|
||||||
|
};
|
||||||
|
DBG("pc: %d, sc: %d\n", pcount, scount);
|
||||||
|
if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong
|
||||||
|
fprintf(stderr, "Wrong helpstring!\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
helpstring = s;
|
||||||
|
DBG("hs: %s\n", helpstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carefull atoll/atoi
|
||||||
|
* @param num (o) - returning value (or NULL if you wish only check number) - allocated by user
|
||||||
|
* @param str (i) - string with number must not be NULL
|
||||||
|
* @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more)
|
||||||
|
* @return TRUE if conversion sone without errors, FALSE otherwise
|
||||||
|
*/
|
||||||
|
bool myatoll(void *num, char *str, argtype t){
|
||||||
|
long long tmp, *llptr;
|
||||||
|
int *iptr;
|
||||||
|
char *endptr;
|
||||||
|
assert(str);
|
||||||
|
assert(num);
|
||||||
|
tmp = strtoll(str, &endptr, 0);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return FALSE;
|
||||||
|
switch(t){
|
||||||
|
case arg_longlong:
|
||||||
|
llptr = (long long*) num;
|
||||||
|
*llptr = tmp;
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
default:
|
||||||
|
if(tmp < INT_MIN || tmp > INT_MAX){
|
||||||
|
fprintf(stderr, "Integer out of range\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
iptr = (int*)num;
|
||||||
|
*iptr = (int)tmp;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same as myatoll but for double
|
||||||
|
// There's no NAN & INF checking here (what if they would be needed?)
|
||||||
|
bool myatod(void *num, const char *str, argtype t){
|
||||||
|
double tmp, *dptr;
|
||||||
|
float *fptr;
|
||||||
|
char *endptr;
|
||||||
|
assert(str);
|
||||||
|
tmp = strtod(str, &endptr);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return FALSE;
|
||||||
|
switch(t){
|
||||||
|
case arg_double:
|
||||||
|
dptr = (double *) num;
|
||||||
|
*dptr = tmp;
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
default:
|
||||||
|
fptr = (float *) num;
|
||||||
|
*fptr = (float)tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get index of current option in array options
|
||||||
|
* @param opt (i) - returning val of getopt_long
|
||||||
|
* @param options (i) - array of options
|
||||||
|
* @return index in array
|
||||||
|
*/
|
||||||
|
int get_optind(int opt, myoption *options){
|
||||||
|
int oind;
|
||||||
|
myoption *opts = options;
|
||||||
|
assert(opts);
|
||||||
|
for(oind = 0; opts->name && opts->val != opt; oind++, opts++);
|
||||||
|
if(!opts->name || opts->val != opt) // no such parameter
|
||||||
|
showhelp(-1, options);
|
||||||
|
return oind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parce command line arguments
|
||||||
|
* ! If arg is string, then value will be strdup'ed!
|
||||||
|
*
|
||||||
|
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
|
||||||
|
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt`
|
||||||
|
* BE CAREFUL! if you wanna use full argc & argv, save their original values before
|
||||||
|
* calling this function
|
||||||
|
* @param options (i) - array of `myoption` for arguments parcing
|
||||||
|
*
|
||||||
|
* @exit: in case of error this function show help & make `exit(-1)`
|
||||||
|
*/
|
||||||
|
void parceargs(int *argc, char ***argv, myoption *options){
|
||||||
|
char *short_options, *soptr;
|
||||||
|
struct option *long_options, *loptr;
|
||||||
|
size_t optsize, i;
|
||||||
|
myoption *opts = options;
|
||||||
|
// check whether there is at least one options
|
||||||
|
assert(opts);
|
||||||
|
assert(opts[0].name);
|
||||||
|
// first we count how much values are in opts
|
||||||
|
for(optsize = 0; opts->name; optsize++, opts++);
|
||||||
|
// now we can allocate memory
|
||||||
|
short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts
|
||||||
|
long_options = calloc(optsize + 1, sizeof(struct option));
|
||||||
|
opts = options; loptr = long_options; soptr = short_options;
|
||||||
|
// fill short/long parameters and make a simple checking
|
||||||
|
for(i = 0; i < optsize; i++, loptr++, opts++){
|
||||||
|
// check
|
||||||
|
assert(opts->name); // check name
|
||||||
|
if(opts->has_arg){
|
||||||
|
assert(opts->type != arg_none); // check error with arg type
|
||||||
|
assert(opts->argptr); // check pointer
|
||||||
|
}
|
||||||
|
if(opts->type != arg_none) // if there is a flag without arg, check its pointer
|
||||||
|
assert(opts->argptr);
|
||||||
|
// fill long_options
|
||||||
|
// don't do memcmp: what if there would be different alignment?
|
||||||
|
loptr->name = opts->name;
|
||||||
|
loptr->has_arg = opts->has_arg;
|
||||||
|
loptr->flag = opts->flag;
|
||||||
|
loptr->val = opts->val;
|
||||||
|
// fill short options if they are:
|
||||||
|
if(!opts->flag){
|
||||||
|
*soptr++ = opts->val;
|
||||||
|
if(opts->has_arg) // add ':' if option has required argument
|
||||||
|
*soptr++ = ':';
|
||||||
|
if(opts->has_arg == 2) // add '::' if option has optional argument
|
||||||
|
*soptr++ = ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now we have both long_options & short_options and can parse `getopt_long`
|
||||||
|
while(1){
|
||||||
|
int opt;
|
||||||
|
int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[]
|
||||||
|
if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break;
|
||||||
|
if(opt == '?'){
|
||||||
|
opt = optopt;
|
||||||
|
optind = get_optind(opt, options);
|
||||||
|
if(options[optind].has_arg == 1) showhelp(optind, options); // need argument
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(opt == 0 || oindex > 0) optind = oindex;
|
||||||
|
else optind = get_optind(opt, options);
|
||||||
|
}
|
||||||
|
opts = &options[optind];
|
||||||
|
DBG ("\n*******\noption %s (oindex = %d / optind = %d)", options[optind].name, oindex, optind);
|
||||||
|
if(optarg) DBG (" with arg %s", optarg);
|
||||||
|
DBG ("\n");
|
||||||
|
if(opt == 0 && opts->has_arg == 0) continue; // only long option changing integer flag
|
||||||
|
DBG("opt = %c, arg type: ", opt);
|
||||||
|
// now check option
|
||||||
|
if(opts->has_arg == 1) assert(optarg);
|
||||||
|
bool result = TRUE;
|
||||||
|
// even if there is no argument, but argptr != NULL, think that optarg = "1"
|
||||||
|
if(!optarg) optarg = "1";
|
||||||
|
switch(opts->type){
|
||||||
|
default:
|
||||||
|
case arg_none:
|
||||||
|
DBG("none\n");
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
DBG("integer\n");
|
||||||
|
result = myatoll(opts->argptr, optarg, arg_int);
|
||||||
|
break;
|
||||||
|
case arg_longlong:
|
||||||
|
DBG("long long\n");
|
||||||
|
result = myatoll(opts->argptr, optarg, arg_longlong);
|
||||||
|
break;
|
||||||
|
case arg_double:
|
||||||
|
DBG("double\n");
|
||||||
|
result = myatod(opts->argptr, optarg, arg_double);
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
DBG("double\n");
|
||||||
|
result = myatod(opts->argptr, optarg, arg_float);
|
||||||
|
break;
|
||||||
|
case arg_string:
|
||||||
|
DBG("string\n");
|
||||||
|
result = (*((char **)opts->argptr) = strdup(optarg));
|
||||||
|
break;
|
||||||
|
case arg_function:
|
||||||
|
DBG("function\n");
|
||||||
|
result = ((argfn)opts->argptr)(optarg, optind);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!result){
|
||||||
|
DBG("OOOPS! Error in result\n");
|
||||||
|
showhelp(optind, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*argc -= optind;
|
||||||
|
*argv += optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show help information based on myoption->help values
|
||||||
|
* @param oindex (i) - if non-negative, show only help by myoption[oindex].help
|
||||||
|
* @param options (i) - array of `myoption`
|
||||||
|
*
|
||||||
|
* @exit: run `exit(-1)` !!!
|
||||||
|
*/
|
||||||
|
void showhelp(int oindex, myoption *options){
|
||||||
|
// ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
|
||||||
|
// but you can redefine it before `#include "parceargs.h"`
|
||||||
|
int max_opt_len = 0; // max len of options substring - for right indentation
|
||||||
|
const int bufsz = 255;
|
||||||
|
char buf[bufsz+1];
|
||||||
|
myoption *opts = options;
|
||||||
|
assert(opts);
|
||||||
|
assert(opts[0].name); // check whether there is at least one options
|
||||||
|
if(oindex > -1){ // print only one message
|
||||||
|
opts = &options[oindex];
|
||||||
|
printf(" ");
|
||||||
|
if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val);
|
||||||
|
printf("--%s", opts->name);
|
||||||
|
if(opts->has_arg == 1) printf("=arg");
|
||||||
|
else if(opts->has_arg == 2) printf("[=arg]");
|
||||||
|
printf(" %s\n", PRNT(opts->help));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// header, by default is just "progname\n"
|
||||||
|
printf("\n");
|
||||||
|
if(strstr(helpstring, "%s")) // print progname
|
||||||
|
printf(helpstring, __progname);
|
||||||
|
else // only text
|
||||||
|
printf("%s", helpstring);
|
||||||
|
printf("\n");
|
||||||
|
// count max_opt_len
|
||||||
|
do{
|
||||||
|
int L = strlen(opts->name);
|
||||||
|
if(max_opt_len < L) max_opt_len = L;
|
||||||
|
}while((++opts)->name);
|
||||||
|
max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols
|
||||||
|
opts = options;
|
||||||
|
// Now print all help
|
||||||
|
do{
|
||||||
|
int p = sprintf(buf, " "); // a little indent
|
||||||
|
if(!opts->flag && isalpha(opts->val)) // .val is short argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val);
|
||||||
|
p += snprintf(buf+p, bufsz-p, "--%s", opts->name);
|
||||||
|
if(opts->has_arg == 1) // required argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "=arg");
|
||||||
|
else if(opts->has_arg == 2) // optional argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "[=arg]");
|
||||||
|
assert(p < max_opt_len); // there would be magic if p >= max_opt_len
|
||||||
|
printf("%-*s%s\n", max_opt_len+1, buf, PRNT(opts->help)); // write options & at least 2 spaces after
|
||||||
|
}while((++opts)->name);
|
||||||
|
printf("\n\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
105
kicad-copy/parceargs.h
Normal file
105
kicad-copy/parceargs.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* parceargs.h - headers for parcing command line arguments
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __PARCEARGS_H__
|
||||||
|
#define __PARCEARGS_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>// bool
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macro for argptr
|
||||||
|
#define APTR(x) ((void*)x)
|
||||||
|
|
||||||
|
// if argptr is a function:
|
||||||
|
typedef bool(*argfn)(void *arg, int N);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* type of getopt's argument
|
||||||
|
* WARNING!
|
||||||
|
* My function change value of flags by pointer, so if you want to use another type
|
||||||
|
* make a latter conversion, example:
|
||||||
|
* char charg;
|
||||||
|
* int iarg;
|
||||||
|
* myoption opts[] = {
|
||||||
|
* {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option};
|
||||||
|
* ..(parce args)..
|
||||||
|
* charg = (char) iarg;
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
arg_none = 0, // no arg
|
||||||
|
arg_int, // integer
|
||||||
|
arg_longlong, // long long
|
||||||
|
arg_double, // double
|
||||||
|
arg_float, // float
|
||||||
|
arg_string, // char *
|
||||||
|
arg_function // parce_args will run function `bool (*fn)(char *optarg, int N)`
|
||||||
|
} argtype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure for getopt_long & help
|
||||||
|
* BE CAREFUL: .argptr is pointer to data or pointer to function,
|
||||||
|
* conversion depends on .type
|
||||||
|
*
|
||||||
|
* ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
|
||||||
|
* but you can redefine it before `#include "parceargs.h"`
|
||||||
|
*
|
||||||
|
* if arg is string, then value wil be strdup'ed like that:
|
||||||
|
* char *str;
|
||||||
|
* myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option};
|
||||||
|
* *(opts[1].str) = strdup(optarg);
|
||||||
|
* in other cases argptr should be address of some variable (or pointer to allocated memory)
|
||||||
|
*
|
||||||
|
* NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr
|
||||||
|
*
|
||||||
|
* !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct{
|
||||||
|
// these are from struct option:
|
||||||
|
const char *name; // long option's name
|
||||||
|
int has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg
|
||||||
|
int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0)
|
||||||
|
int val; // short opt name (if flag == NULL) or flag's value
|
||||||
|
// and these are mine:
|
||||||
|
argtype type; // type of argument
|
||||||
|
void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)`
|
||||||
|
char *help; // help string which would be shown in function `showhelp` or NULL
|
||||||
|
} myoption;
|
||||||
|
|
||||||
|
// last string of array (all zeros)
|
||||||
|
#define end_option {0,0,0,0,0,0,0}
|
||||||
|
|
||||||
|
|
||||||
|
extern const char *__progname;
|
||||||
|
|
||||||
|
void showhelp(int oindex, myoption *options);
|
||||||
|
void parceargs(int *argc, char ***argv, myoption *options);
|
||||||
|
void change_helpstring(char *s);
|
||||||
|
|
||||||
|
#endif // __PARCEARGS_H__
|
||||||
300
kicad-copy/usefull_macros.c
Normal file
300
kicad-copy/usefull_macros.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* usefull_macros.h - a set of usefull functions: memory, color etc
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Coloured terminal
|
||||||
|
\******************************************************************************/
|
||||||
|
int globErr = 0; // errno for WARN/ERR
|
||||||
|
|
||||||
|
// pointers to coloured output printf
|
||||||
|
int (*red)(const char *fmt, ...);
|
||||||
|
int (*green)(const char *fmt, ...);
|
||||||
|
int (*_WARN)(const char *fmt, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* format red / green messages
|
||||||
|
* name: r_pr_, g_pr_
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int r_pr_(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
printf(RED);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf(OLDCOLOR);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int g_pr_(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
printf(GREEN);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf(OLDCOLOR);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* print red error/warning messages (if output is a tty)
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int r_WARN(const char *fmt, ...){
|
||||||
|
va_list ar; int i = 1;
|
||||||
|
fprintf(stderr, RED);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if(globErr){
|
||||||
|
errno = globErr;
|
||||||
|
vwarn(fmt, ar);
|
||||||
|
errno = 0;
|
||||||
|
globErr = 0;
|
||||||
|
}else
|
||||||
|
i = vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i++;
|
||||||
|
fprintf(stderr, OLDCOLOR "\n");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char stars[] = "****************************************";
|
||||||
|
/*
|
||||||
|
* notty variants of coloured printf
|
||||||
|
* name: s_WARN, r_pr_notty
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int s_WARN(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
i = fprintf(stderr, "\n%s\n", stars);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if(globErr){
|
||||||
|
errno = globErr;
|
||||||
|
vwarn(fmt, ar);
|
||||||
|
errno = 0;
|
||||||
|
globErr = 0;
|
||||||
|
}else
|
||||||
|
i = +vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i += fprintf(stderr, "\n%s\n", stars);
|
||||||
|
i += fprintf(stderr, "\n");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int r_pr_notty(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
i = printf("\n%s\n", stars);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i += vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i += printf("\n%s\n", stars);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run this function in the beginning of main() to setup locale & coloured output
|
||||||
|
*/
|
||||||
|
void initial_setup(){
|
||||||
|
// setup coloured output
|
||||||
|
if(isatty(STDOUT_FILENO)){ // make color output in tty
|
||||||
|
red = r_pr_; green = g_pr_;
|
||||||
|
}else{ // no colors in case of pipe
|
||||||
|
red = r_pr_notty; green = printf;
|
||||||
|
}
|
||||||
|
if(isatty(STDERR_FILENO)) _WARN = r_WARN;
|
||||||
|
else _WARN = s_WARN;
|
||||||
|
// Setup locale
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
textdomain(GETTEXT_PACKAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Memory
|
||||||
|
\******************************************************************************/
|
||||||
|
/*
|
||||||
|
* safe memory allocation for macro ALLOC
|
||||||
|
* @param N - number of elements to allocate
|
||||||
|
* @param S - size of single element (typically sizeof)
|
||||||
|
* @return pointer to allocated memory area
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) ERR("malloc");
|
||||||
|
//assert(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Mmap file to a memory area
|
||||||
|
*
|
||||||
|
* @param filename (i) - name of file to mmap
|
||||||
|
* @return stuct with mmap'ed file or die
|
||||||
|
*/
|
||||||
|
mmapbuf *My_mmap(char *filename){
|
||||||
|
int fd;
|
||||||
|
char *ptr;
|
||||||
|
size_t Mlen;
|
||||||
|
struct stat statbuf;
|
||||||
|
if(!filename) ERRX(_("No filename given!"));
|
||||||
|
if((fd = open(filename, O_RDONLY)) < 0)
|
||||||
|
ERR(_("Can't open %s for reading"), filename);
|
||||||
|
if(fstat (fd, &statbuf) < 0)
|
||||||
|
ERR(_("Can't stat %s"), filename);
|
||||||
|
Mlen = statbuf.st_size;
|
||||||
|
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
|
||||||
|
ERR(_("Mmap error for input"));
|
||||||
|
if(close(fd)) ERR(_("Can't close mmap'ed file"));
|
||||||
|
mmapbuf *ret = MALLOC(mmapbuf, 1);
|
||||||
|
ret->data = ptr;
|
||||||
|
ret->len = Mlen;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void My_munmap(mmapbuf *b){
|
||||||
|
if(munmap(b->data, b->len))
|
||||||
|
ERR(_("Can't munmap"));
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Terminal in no-echo mode
|
||||||
|
\******************************************************************************/
|
||||||
|
struct termios oldt, newt; // terminal flags
|
||||||
|
// run on exit:
|
||||||
|
/*
|
||||||
|
void quit(int sig){
|
||||||
|
//...
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// initial setup:
|
||||||
|
void setup_con(){
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
exit(-2); //quit?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read character from console without echo
|
||||||
|
* @return char readed
|
||||||
|
*/
|
||||||
|
int read_console(){
|
||||||
|
int rb;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(STDIN_FILENO, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 10000;
|
||||||
|
retval = select(1, &rfds, NULL, NULL, &tv);
|
||||||
|
if(!retval) rb = 0;
|
||||||
|
else {
|
||||||
|
if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar();
|
||||||
|
else rb = 0;
|
||||||
|
}
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getchar() without echo
|
||||||
|
* wait until at least one character pressed
|
||||||
|
* @return character readed
|
||||||
|
*/
|
||||||
|
int mygetchar(){ // аналог getchar() без необходимости жать Enter
|
||||||
|
int ret;
|
||||||
|
do ret = read_console();
|
||||||
|
while(ret == 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* TTY with select()
|
||||||
|
\******************************************************************************/
|
||||||
|
struct termio oldtty, tty; // TTY flags
|
||||||
|
char *comdev; // TTY device name
|
||||||
|
int comfd; // TTY fd
|
||||||
|
// run on exit:
|
||||||
|
/*
|
||||||
|
void quit(int ex_stat){
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifndef BAUD_RATE
|
||||||
|
#define BAUD_RATE B9600
|
||||||
|
#endif
|
||||||
|
// init:
|
||||||
|
void tty_init(){
|
||||||
|
printf("\nOpen port...\n");
|
||||||
|
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
|
||||||
|
fprintf(stderr,"Can't use port %s\n",comdev);
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
exit(1); // quit?
|
||||||
|
}
|
||||||
|
printf(" OK\nGet current settings...\n");
|
||||||
|
if(ioctl(comfd,TCGETA,&oldtty) < 0) exit(-1); // Get settings
|
||||||
|
tty = oldtty;
|
||||||
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
|
tty.c_oflag = 0;
|
||||||
|
tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
|
||||||
|
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
|
tty.c_cc[VTIME] = 5;
|
||||||
|
if(ioctl(comfd,TCSETA,&tty) < 0) exit(-1); // set new mode
|
||||||
|
printf(" OK\n");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Read data from TTY
|
||||||
|
* @param buff (o) - buffer for data read
|
||||||
|
* @param length - buffer len
|
||||||
|
* @return amount of readed bytes
|
||||||
|
*/
|
||||||
|
size_t read_tty(uint8_t *buff, size_t length){
|
||||||
|
ssize_t L = 0;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(comfd, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 50000; // wait for 50ms
|
||||||
|
retval = select(comfd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if (!retval) return 0;
|
||||||
|
if(FD_ISSET(comfd, &rfds)){
|
||||||
|
if((L = read(comfd, buff, length)) < 1) return 0;
|
||||||
|
}
|
||||||
|
return (size_t)L;
|
||||||
|
}
|
||||||
106
kicad-copy/usefull_macros.h
Normal file
106
kicad-copy/usefull_macros.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* usefull_macros.h - a set of usefull macros: memory, color etc
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __USEFULL_MACROS_H__
|
||||||
|
#define __USEFULL_MACROS_H__
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <libintl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <termio.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GETTEXT
|
||||||
|
*/
|
||||||
|
#define _(String) gettext(String)
|
||||||
|
#define gettext_noop(String) String
|
||||||
|
#define N_(String) gettext_noop(String)
|
||||||
|
|
||||||
|
// unused arguments with -Wall -Werror
|
||||||
|
#define _U_ __attribute__((__unused__))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coloured messages output
|
||||||
|
*/
|
||||||
|
#define RED "\033[1;31;40m"
|
||||||
|
#define GREEN "\033[1;32;40m"
|
||||||
|
#define OLDCOLOR "\033[0;0;0m"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ERROR/WARNING messages
|
||||||
|
*/
|
||||||
|
extern int globErr;
|
||||||
|
#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); exit(-1);}while(0)
|
||||||
|
#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); exit(-1);}while(0)
|
||||||
|
#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0)
|
||||||
|
#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print function name, debug messages
|
||||||
|
* debug mode, -DEBUG
|
||||||
|
*/
|
||||||
|
#ifdef EBUG
|
||||||
|
#define FNAME() fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
|
||||||
|
#define DBG(...) do{fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, "\n");} while(0)
|
||||||
|
#else
|
||||||
|
#define FNAME() do{}while(0)
|
||||||
|
#define DBG(...) do{}while(0)
|
||||||
|
#endif //EBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory allocation
|
||||||
|
*/
|
||||||
|
#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type)))
|
||||||
|
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
|
||||||
|
#define FREE(ptr) do{free(ptr); ptr = NULL;}while(0)
|
||||||
|
|
||||||
|
// functions for color output in tty & no-color in pipes
|
||||||
|
extern int (*red)(const char *fmt, ...);
|
||||||
|
extern int (*_WARN)(const char *fmt, ...);
|
||||||
|
extern int (*green)(const char *fmt, ...);
|
||||||
|
void * my_alloc(size_t N, size_t S);
|
||||||
|
void initial_setup();
|
||||||
|
|
||||||
|
// mmap file
|
||||||
|
typedef struct{
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
} mmapbuf;
|
||||||
|
mmapbuf *My_mmap(char *filename);
|
||||||
|
void My_munmap(mmapbuf *b);
|
||||||
|
|
||||||
|
#endif // __USEFULL_MACROS_H__
|
||||||
32
simple_gettext/1.c
Normal file
32
simple_gettext/1.c
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <libintl.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#define _(String) gettext(String)
|
||||||
|
#define gettext_noop(String) String
|
||||||
|
#define N_(String) gettext_noop(String)
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
/*
|
||||||
|
* To run in GUI bullshit (like qt or gtk) you must do:
|
||||||
|
* bind_textdomain_codeset(PACKAGE, "UTF8");
|
||||||
|
*/
|
||||||
|
textdomain(GETTEXT_PACKAGE);
|
||||||
|
/*
|
||||||
|
* In case when you need both GUI and CLI, you should
|
||||||
|
* do this:
|
||||||
|
* char*old = bind_textdomain_codeset (PACKAGE, "");
|
||||||
|
* before printf calling
|
||||||
|
* To restore GUI hrunicode do
|
||||||
|
* bind_textdomain_codeset (PACKAGE, old);
|
||||||
|
* at the end of printf's
|
||||||
|
*/
|
||||||
|
/// ðÅÒÅ×ÅÄÅÎÏ ÇÅÔÔÅËÓÔÏÍ\n
|
||||||
|
printf(_("Translated by gettext\n"));
|
||||||
|
/// ÷ÐÏÌÎÅ ÒÁÂÏÔÁÅÔ × ÌÀÂÏÊ ËÏÄÉÒÏ×ËÅ\n
|
||||||
|
printf(_("This works fine in any charset\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
31
simple_gettext/Makefile
Normal file
31
simple_gettext/Makefile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
PROGRAM = test
|
||||||
|
LDFLAGS =
|
||||||
|
DEFINES = -D_XOPEN_SOURCE=501 -DGETTEXT_PACKAGE=\"$(PROGRAM)\" -DLOCALEDIR=\".\"
|
||||||
|
SRCS = 1.c
|
||||||
|
CC = gcc
|
||||||
|
CXX = gcc
|
||||||
|
CFLAGS = -Wall -Werror $(DEFINES)
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
PO_FILE = $(PROGRAM).po
|
||||||
|
RU_FILE = $(PROGRAM)_ru.po
|
||||||
|
LDIR = "ru/LC_MESSAGES"
|
||||||
|
MO_FILE = $(LDIR)/$(PROGRAM).mo
|
||||||
|
|
||||||
|
all : $(PROGRAM) $(MO_FILE)
|
||||||
|
|
||||||
|
$(PROGRAM) : $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
$(PO_FILE): $(SRCS)
|
||||||
|
xgettext -D. --from-code=koi8-r $(SRCS) -c -k_ -kN_ -o $(PO_FILE)
|
||||||
|
sed -i 's/charset=.*\\n/charset=koi8-r\\n/' $(PO_FILE) && enconv $(PO_FILE)
|
||||||
|
$(RU_FILE): $(PO_FILE)
|
||||||
|
[ -e $(RU_FILE) ] && msgmerge -Uis $(RU_FILE) $(PO_FILE) || cp $(PO_FILE) $(RU_FILE)
|
||||||
|
$(MO_FILE): $(LDIR) $(RU_FILE)
|
||||||
|
msgfmt $(RU_FILE) -o $(MO_FILE)
|
||||||
|
$(LDIR):
|
||||||
|
[ -e $(LDIR) ] || mkdir -p $(LDIR)
|
||||||
|
clean:
|
||||||
|
/bin/rm -f *.o *~
|
||||||
|
depend:
|
||||||
|
$(CXX) -MM $(CXX.SRCS)
|
||||||
29
simple_gettext/test_ru.po
Normal file
29
simple_gettext/test_ru.po
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr "Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2014-04-24 18:13+0400\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=koi8-r\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#. / ÷ÐÏÌÎÅ ÒÁÂÏÔÁÅÔ × ÌÀÂÏÊ ËÏÄÉÒÏ×ËÅ\n
|
||||||
|
#: 1.c:17
|
||||||
|
#, c-format
|
||||||
|
msgid "This works fine in any charset\n"
|
||||||
|
msgstr "÷ÐÏÌÎÅ ÒÁÂÏÔÁÅÔ × ÌÀÂÏÊ ËÏÄÉÒÏ×ËÅ\n"
|
||||||
|
|
||||||
|
#. / ðÅÒÅ×ÅÄÅÎÏ ÇÅÔÔÅËÓÔÏÍ\n
|
||||||
|
#: 1.c:15
|
||||||
|
#, c-format
|
||||||
|
msgid "Translated by gettext\n"
|
||||||
|
msgstr "ðÅÒÅ×ÅÄÅÎÏ ÇÅÔÔÅËÓÔÏÍ\n"
|
||||||
112
simple_list.c
Normal file
112
simple_list.c
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* simple_list.c - simple one-direction list
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
#include "simple_list.h"
|
||||||
|
|
||||||
|
#define MALLOC alloc_simple_list
|
||||||
|
/**
|
||||||
|
* Memory allocation with control
|
||||||
|
* @param N - number of elements
|
||||||
|
* @param S - size of one element
|
||||||
|
* @return allocated memory or die
|
||||||
|
*/
|
||||||
|
static void *alloc_simple_list(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) err(1, "malloc");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (*listdata_free)(listdata node_data) = NULL; // external function to free(listdata)
|
||||||
|
|
||||||
|
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add element v to list with root root (also this can be last element)
|
||||||
|
* @param root (io) - pointer to root (or last element) of list or NULL
|
||||||
|
* if *root == NULL, just created node will be placed there
|
||||||
|
* @param v - data inserted
|
||||||
|
* @return pointer to created node
|
||||||
|
*/
|
||||||
|
List *list_add(List **root, listdata v){
|
||||||
|
List *node, *last;
|
||||||
|
if((node = (List*) MALLOC(1, sizeof(List))) == 0) return NULL; // allocation error
|
||||||
|
node->data = v; // insert data
|
||||||
|
if(root){
|
||||||
|
if(*root){ // there was root node - search last
|
||||||
|
last = *root;
|
||||||
|
while(last->next) last = last->next;
|
||||||
|
last->next = node; // insert pointer to new node into last element in list
|
||||||
|
}
|
||||||
|
if(!*root) *root = node;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set listdata_free()
|
||||||
|
* @param fn - function that freec memory of (node)
|
||||||
|
*/
|
||||||
|
void listfree_function(void (*fn)(listdata node)){
|
||||||
|
listdata_free = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove all nodes in list
|
||||||
|
* @param root - pointer to root node
|
||||||
|
*/
|
||||||
|
void list_free(List **root){
|
||||||
|
List *node = *root, *next;
|
||||||
|
if(!root || !*root) return;
|
||||||
|
do{
|
||||||
|
next = node->next;
|
||||||
|
if(listdata_free)
|
||||||
|
listdata_free(node->data);
|
||||||
|
free(node);
|
||||||
|
node = next;
|
||||||
|
}while(node);
|
||||||
|
*root = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
int main(void) {
|
||||||
|
List *tp = NULL, *root_p = NULL;
|
||||||
|
int i, ins[] = {4,2,6,1,3,4,7};
|
||||||
|
for(i = 0; i < 7; i++){
|
||||||
|
if(!(tp = list_add(&tp, ins[i])))
|
||||||
|
err(1, "Malloc error"); // can't insert
|
||||||
|
if(!root_p) root_p = tp;
|
||||||
|
}
|
||||||
|
tp = root_p;
|
||||||
|
i = 0;
|
||||||
|
do{
|
||||||
|
printf("element %d = %d\n", i++, tp->data);
|
||||||
|
tp = tp->next;
|
||||||
|
}while(tp);
|
||||||
|
list_free(&root_p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
44
simple_list.h
Normal file
44
simple_list.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* simple_list.h - header file for simple list support
|
||||||
|
* TO USE IT you must define the type of data as
|
||||||
|
* typedef your_type listdata
|
||||||
|
* or at compiling time
|
||||||
|
* -Dlistdata=your_type
|
||||||
|
* or by changing this file
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __SIMPLE_LIST_H__
|
||||||
|
#define __SIMPLE_LIST_H__
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
typedef int listdata;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct list_{
|
||||||
|
listdata data;
|
||||||
|
struct list_ *next;
|
||||||
|
} List;
|
||||||
|
|
||||||
|
// add element v to list with root root (also this can be last element)
|
||||||
|
List *list_add(List **root, listdata v);
|
||||||
|
// set listdata_free()
|
||||||
|
void listfree_function(void (*fn)(listdata node));
|
||||||
|
|
||||||
|
#endif // __SIMPLE_LIST_H__
|
||||||
300
usefull_macros.c
Normal file
300
usefull_macros.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* usefull_macros.h - a set of usefull functions: memory, color etc
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Coloured terminal
|
||||||
|
\******************************************************************************/
|
||||||
|
int globErr = 0; // errno for WARN/ERR
|
||||||
|
|
||||||
|
// pointers to coloured output printf
|
||||||
|
int (*red)(const char *fmt, ...);
|
||||||
|
int (*green)(const char *fmt, ...);
|
||||||
|
int (*_WARN)(const char *fmt, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* format red / green messages
|
||||||
|
* name: r_pr_, g_pr_
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int r_pr_(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
printf(RED);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf(OLDCOLOR);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int g_pr_(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
printf(GREEN);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf(OLDCOLOR);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* print red error/warning messages (if output is a tty)
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int r_WARN(const char *fmt, ...){
|
||||||
|
va_list ar; int i = 1;
|
||||||
|
fprintf(stderr, RED);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if(globErr){
|
||||||
|
errno = globErr;
|
||||||
|
vwarn(fmt, ar);
|
||||||
|
errno = 0;
|
||||||
|
globErr = 0;
|
||||||
|
}else
|
||||||
|
i = vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i++;
|
||||||
|
fprintf(stderr, OLDCOLOR "\n");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char stars[] = "****************************************";
|
||||||
|
/*
|
||||||
|
* notty variants of coloured printf
|
||||||
|
* name: s_WARN, r_pr_notty
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int s_WARN(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
i = fprintf(stderr, "\n%s\n", stars);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if(globErr){
|
||||||
|
errno = globErr;
|
||||||
|
vwarn(fmt, ar);
|
||||||
|
errno = 0;
|
||||||
|
globErr = 0;
|
||||||
|
}else
|
||||||
|
i = +vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i += fprintf(stderr, "\n%s\n", stars);
|
||||||
|
i += fprintf(stderr, "\n");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int r_pr_notty(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
i = printf("\n%s\n", stars);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i += vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i += printf("\n%s\n", stars);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run this function in the beginning of main() to setup locale & coloured output
|
||||||
|
*/
|
||||||
|
void initial_setup(){
|
||||||
|
// setup coloured output
|
||||||
|
if(isatty(STDOUT_FILENO)){ // make color output in tty
|
||||||
|
red = r_pr_; green = g_pr_;
|
||||||
|
}else{ // no colors in case of pipe
|
||||||
|
red = r_pr_notty; green = printf;
|
||||||
|
}
|
||||||
|
if(isatty(STDERR_FILENO)) _WARN = r_WARN;
|
||||||
|
else _WARN = s_WARN;
|
||||||
|
// Setup locale
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
textdomain(GETTEXT_PACKAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Memory
|
||||||
|
\******************************************************************************/
|
||||||
|
/*
|
||||||
|
* safe memory allocation for macro ALLOC
|
||||||
|
* @param N - number of elements to allocate
|
||||||
|
* @param S - size of single element (typically sizeof)
|
||||||
|
* @return pointer to allocated memory area
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) ERR("malloc");
|
||||||
|
//assert(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Mmap file to a memory area
|
||||||
|
*
|
||||||
|
* @param filename (i) - name of file to mmap
|
||||||
|
* @return stuct with mmap'ed file or die
|
||||||
|
*/
|
||||||
|
mmapbuf *My_mmap(char *filename){
|
||||||
|
int fd;
|
||||||
|
char *ptr;
|
||||||
|
size_t Mlen;
|
||||||
|
struct stat statbuf;
|
||||||
|
if(!filename) ERRX(_("No filename given!"));
|
||||||
|
if((fd = open(filename, O_RDONLY)) < 0)
|
||||||
|
ERR(_("Can't open %s for reading"), filename);
|
||||||
|
if(fstat (fd, &statbuf) < 0)
|
||||||
|
ERR(_("Can't stat %s"), filename);
|
||||||
|
Mlen = statbuf.st_size;
|
||||||
|
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
|
||||||
|
ERR(_("Mmap error for input"));
|
||||||
|
if(close(fd)) ERR(_("Can't close mmap'ed file"));
|
||||||
|
mmapbuf *ret = MALLOC(mmapbuf, 1);
|
||||||
|
ret->data = ptr;
|
||||||
|
ret->len = Mlen;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void My_munmap(mmapbuf *b){
|
||||||
|
if(munmap(b->data, b->len))
|
||||||
|
ERR(_("Can't munmap"));
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Terminal in no-echo mode
|
||||||
|
\******************************************************************************/
|
||||||
|
struct termios oldt, newt; // terminal flags
|
||||||
|
// run on exit:
|
||||||
|
/*
|
||||||
|
void quit(int sig){
|
||||||
|
//...
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// initial setup:
|
||||||
|
void setup_con(){
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
exit(-2); //quit?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read character from console without echo
|
||||||
|
* @return char readed
|
||||||
|
*/
|
||||||
|
int read_console(){
|
||||||
|
int rb;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(STDIN_FILENO, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 10000;
|
||||||
|
retval = select(1, &rfds, NULL, NULL, &tv);
|
||||||
|
if(!retval) rb = 0;
|
||||||
|
else {
|
||||||
|
if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar();
|
||||||
|
else rb = 0;
|
||||||
|
}
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getchar() without echo
|
||||||
|
* wait until at least one character pressed
|
||||||
|
* @return character readed
|
||||||
|
*/
|
||||||
|
int mygetchar(){ // аналог getchar() без необходимости жать Enter
|
||||||
|
int ret;
|
||||||
|
do ret = read_console();
|
||||||
|
while(ret == 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* TTY with select()
|
||||||
|
\******************************************************************************/
|
||||||
|
struct termio oldtty, tty; // TTY flags
|
||||||
|
char *comdev; // TTY device name
|
||||||
|
int comfd; // TTY fd
|
||||||
|
// run on exit:
|
||||||
|
/*
|
||||||
|
void quit(int ex_stat){
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifndef BAUD_RATE
|
||||||
|
#define BAUD_RATE B9600
|
||||||
|
#endif
|
||||||
|
// init:
|
||||||
|
void tty_init(){
|
||||||
|
printf("\nOpen port...\n");
|
||||||
|
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
|
||||||
|
fprintf(stderr,"Can't use port %s\n",comdev);
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
exit(1); // quit?
|
||||||
|
}
|
||||||
|
printf(" OK\nGet current settings...\n");
|
||||||
|
if(ioctl(comfd,TCGETA,&oldtty) < 0) exit(-1); // Get settings
|
||||||
|
tty = oldtty;
|
||||||
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
|
tty.c_oflag = 0;
|
||||||
|
tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
|
||||||
|
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
|
tty.c_cc[VTIME] = 5;
|
||||||
|
if(ioctl(comfd,TCSETA,&tty) < 0) exit(-1); // set new mode
|
||||||
|
printf(" OK\n");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Read data from TTY
|
||||||
|
* @param buff (o) - buffer for data read
|
||||||
|
* @param length - buffer len
|
||||||
|
* @return amount of readed bytes
|
||||||
|
*/
|
||||||
|
size_t read_tty(uint8_t *buff, size_t length){
|
||||||
|
ssize_t L = 0;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(comfd, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 50000; // wait for 50ms
|
||||||
|
retval = select(comfd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if (!retval) return 0;
|
||||||
|
if(FD_ISSET(comfd, &rfds)){
|
||||||
|
if((L = read(comfd, buff, length)) < 1) return 0;
|
||||||
|
}
|
||||||
|
return (size_t)L;
|
||||||
|
}
|
||||||
106
usefull_macros.h
Normal file
106
usefull_macros.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* usefull_macros.h - a set of usefull macros: memory, color etc
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __USEFULL_MACROS_H__
|
||||||
|
#define __USEFULL_MACROS_H__
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <libintl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <termio.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GETTEXT
|
||||||
|
*/
|
||||||
|
#define _(String) gettext(String)
|
||||||
|
#define gettext_noop(String) String
|
||||||
|
#define N_(String) gettext_noop(String)
|
||||||
|
|
||||||
|
// unused arguments with -Wall -Werror
|
||||||
|
#define _U_ __attribute__((__unused__))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coloured messages output
|
||||||
|
*/
|
||||||
|
#define RED "\033[1;31;40m"
|
||||||
|
#define GREEN "\033[1;32;40m"
|
||||||
|
#define OLDCOLOR "\033[0;0;0m"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ERROR/WARNING messages
|
||||||
|
*/
|
||||||
|
extern int globErr;
|
||||||
|
#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); exit(-1);}while(0)
|
||||||
|
#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); exit(-1);}while(0)
|
||||||
|
#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0)
|
||||||
|
#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print function name, debug messages
|
||||||
|
* debug mode, -DEBUG
|
||||||
|
*/
|
||||||
|
#ifdef EBUG
|
||||||
|
#define FNAME() fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
|
||||||
|
#define DBG(...) do{fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, "\n");} while(0)
|
||||||
|
#else
|
||||||
|
#define FNAME() do{}while(0)
|
||||||
|
#define DBG(...) do{}while(0)
|
||||||
|
#endif //EBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory allocation
|
||||||
|
*/
|
||||||
|
#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type)))
|
||||||
|
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
|
||||||
|
#define FREE(ptr) do{free(ptr); ptr = NULL;}while(0)
|
||||||
|
|
||||||
|
// functions for color output in tty & no-color in pipes
|
||||||
|
extern int (*red)(const char *fmt, ...);
|
||||||
|
extern int (*_WARN)(const char *fmt, ...);
|
||||||
|
extern int (*green)(const char *fmt, ...);
|
||||||
|
void * my_alloc(size_t N, size_t S);
|
||||||
|
void initial_setup();
|
||||||
|
|
||||||
|
// mmap file
|
||||||
|
typedef struct{
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
} mmapbuf;
|
||||||
|
mmapbuf *My_mmap(char *filename);
|
||||||
|
void My_munmap(mmapbuf *b);
|
||||||
|
|
||||||
|
#endif // __USEFULL_MACROS_H__
|
||||||
Loading…
x
Reference in New Issue
Block a user