restructurization

This commit is contained in:
2026-05-28 14:23:39 +03:00
parent aca7e3617d
commit b493b36948
211 changed files with 28 additions and 161 deletions

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "errors.h"
#include <stdlib.h>
#include <stdio.h>
static const abortcodes AC[] = {
//while read l; do N=$(echo $l|awk '{print $1 $2}'); R=$(echo $l|awk '{$1=$2=""; print substr($0,3)}'|sed 's/\.//'); echo -e "{0x$N, \"$R\"},"; done < codes.b
{0x05030000, "Toggle bit not alternated"},
{0x05040000, "SDO protocol timed out"},
{0x05040001, "Client/server command specifier not valid or unknown"},
{0x05040002, "Invalid block size (block mode only)"},
{0x05040003, "Invalid sequence number (block mode only)"},
{0x05040004, "CRC error (block mode only)"},
{0x05040005, "Out of memory"},
{0x06010000, "Unsupported access to an object"},
{0x06010001, "Attempt to read a write only object"},
{0x06010002, "Attempt to write a read only object"},
{0x06020000, "Object does not exist in the object dictionary"},
{0x06040041, "Object cannot be mapped to the PDO"},
{0x06040042, "The number and length of the objects to be mapped would exceed PDO length"},
{0x06040043, "General parameter incompatibility reason"},
{0x06040047, "General internal incompatibility in the device"},
{0x06060000, "Access failed due to a hardware error"},
{0x06070010, "Data type does not match; length of service parameter does not match"},
{0x06070012, "Data type does not match; length of service parameter too high"},
{0x06070013, "Data type does not match; length of service parameter too low"},
{0x06090011, "Sub-index does not exist"},
{0x06090030, "Value range of parameter exceeded (only for write access)"},
{0x06090031, "Value of parameter written too high"},
{0x06090032, "Value of parameter written too low"},
{0x06090036, "Maximum value is less than minimum value"},
{0x08000000, "General error"},
{0x08000020, "Data cannot be transferred or stored to the application"},
{0x08000021, "Data cannot be transferred or stored to the application because of local control"},
{0x08000022, "Data cannot be transferred or stored to the application because of the present device state"},
{0x08000023, "Object dictionary dynamic generation fails or no object dictionary is present"},
};
const int ACmax = sizeof(AC)/sizeof(abortcodes) - 1;
const char *ACtext(uint32_t abortcode, int *n){
int idx = ACmax/2, min_ = 0, max_ = ACmax, newidx = 0, iter=0;
do{
++iter;
uint32_t c = AC[idx].code;
printf("idx=%d, min=%d, max=%d\n", idx, min_, max_);
if(c == abortcode){
if(n) *n = iter;
return AC[idx].errmsg;
}else if(c > abortcode){
newidx = (idx + min_)/2;
max_ = idx;
}else{
newidx = (idx + max_ + 1)/2;
min_ = idx;
}
if(newidx == idx || min_ < 0 || max_ > ACmax){
if(n) *n = 0;
return NULL;
}
idx = newidx;
}while(1);
}
void check_all(){
int iter = 0, N;
for(int i = 0; i <= ACmax; ++i){
printf("code 0x%X: %s\n\n", AC[i].code, ACtext(AC[i].code, &N));
iter += N;
}
printf("\n\ntotal: %d iterations, mean: %d, (%d for direct lookup)\n", iter, iter/(ACmax+1), (ACmax+1)/2);
}
int main(int argc, char **argv){
if(argc != 2){
check_all();
return 0;
}
uint32_t x = (uint32_t)strtol(argv[1], NULL, 0);
printf("x=0x%X\n", x);
const char *text = ACtext(x, NULL);
if(text) printf("%s\n", text);
else printf("Unknown error code\n");
return 0;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef ERRORS_H__
#define ERRORS_H__
#include <stdint.h>
typedef struct{
uint32_t code;
char *errmsg;
} abortcodes;
#endif // ERRORS_H__

22
_deprecated/FITS/Makefile Normal file
View File

@@ -0,0 +1,22 @@
PROGRAM = fitsread
LDFLAGS = -lcfitsio
SRCS = $(wildcard *.c)
CC = gcc
DEFINES = -D_XOPEN_SOURCE=1111 -DEBUG
CXX = gcc
CFLAGS = -Wall -Werror -Wextra $(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 $(CXX.SRCS)

1
_deprecated/FITS/README Normal file
View File

@@ -0,0 +1 @@
Simple routines for FITS reading/writing

161
_deprecated/FITS/fits.c Normal file
View File

@@ -0,0 +1,161 @@
/*
* fits.c - cfitsio routines
*
* Copyright 2015 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 <string.h>
#include <stdio.h>
#include <fitsio.h>
#include "fits.h"
#include "usefull_macros.h"
static int fitsstatus = 0;
/*
* Macros for error processing when working with cfitsio functions
*/
#define TRYFITS(f, ...) \
do{ fitsstatus = 0; \
f(__VA_ARGS__, &fitsstatus); \
if(fitsstatus){ \
fits_report_error(stderr, fitsstatus); \
return FALSE;} \
}while(0)
#define FITSFUN(f, ...) \
do{ fitsstatus = 0; \
int ret = f(__VA_ARGS__, &fitsstatus); \
if(ret || fitsstatus) \
fits_report_error(stderr, fitsstatus); \
}while(0)
#define WRITEKEY(...) \
do{ fitsstatus = 0; \
fits_write_key(__VA_ARGS__, &fitsstatus); \
if(status) fits_report_error(stderr, status);\
}while(0)
void imfree(IMAGE **img){
size_t i, sz = (*img)->keynum;
char **list = (*img)->keylist;
for(i = 0; i < sz; ++i) FREE(list[i]);
FREE((*img)->keylist);
FREE((*img)->data);
FREE(*img);
}
bool readFITS(char *filename, IMAGE **fits){
FNAME();
bool ret = TRUE;
fitsfile *fp;
// float nullval = 0., imBits, bZero = 0., bScale = 1.;
int i, j, hdunum, hdutype, nkeys, keypos;
int naxis;
long naxes[2];
char card[FLEN_CARD];
IMAGE *img = MALLOC(IMAGE, 1);
TRYFITS(fits_open_file, &fp, filename, READONLY);
FITSFUN(fits_get_num_hdus, fp, &hdunum);
if(hdunum < 1){
WARNX(_("Can't read HDU"));
ret = FALSE;
goto returning;
}
// get image dimensions
TRYFITS(fits_get_img_param, fp, 2, &img->dtype, &naxis, naxes);
if(naxis > 2){
WARNX(_("Images with > 2 dimensions are not supported"));
ret = FALSE;
goto returning;
}
img->width = naxes[0];
img->height = naxes[1];
DBG("got image %ldx%ld pix, bitpix=%d", naxes[0], naxes[1], img->dtype);
// loop through all HDUs
for(i = 1; !(fits_movabs_hdu(fp, i, &hdutype, &fitsstatus)); ++i){
TRYFITS(fits_get_hdrpos, fp, &nkeys, &keypos);
int oldnkeys = img->keynum;
img->keynum += nkeys;
if(!(img->keylist = realloc(img->keylist, sizeof(char*) * img->keynum))){
ERR(_("Can't realloc"));
}
char **currec = &(img->keylist[oldnkeys]);
DBG("HDU # %d of %d keys", i, nkeys);
for(j = 1; j <= nkeys; ++j){
FITSFUN(fits_read_record, fp, j, card);
if(!fitsstatus){
*currec = MALLOC(char, FLEN_CARD);
memcpy(*currec, card, FLEN_CARD);
DBG("key %d: %s", oldnkeys + j, *currec);
++currec;
}
}
}
if(fitsstatus == END_OF_FILE){
fitsstatus = 0;
}else{
fits_report_error(stderr, fitsstatus);
ret = FALSE;
goto returning;
}
size_t sz = naxes[0] * naxes[1];
img->data = MALLOC(double, sz);
int stat = 0;
TRYFITS(fits_read_img, fp, TDOUBLE, 1, sz, NULL, img->data, &stat);
if(stat) WARNX(_("Found %d pixels with undefined value"), stat);
DBG("ready");
returning:
FITSFUN(fits_close_file, fp);
if(!ret){
imfree(&img);
}
if(fits) *fits = img;
return ret;
}
bool writeFITS(char *filename, IMAGE *fits){
int w = fits->width, h = fits->height;
long naxes[2] = {w, h};
size_t sz = w * h, keys = fits->keynum;
fitsfile *fp;
TRYFITS(fits_create_file, &fp, filename);
TRYFITS(fits_create_img, fp, fits->dtype, 2, naxes);
if(keys){ // there's keys
size_t i;
char **records = fits->keylist;
for(i = 0; i < keys; ++i){
char *rec = records[i];
if(strncmp(rec, "SIMPLE", 6) == 0 || strncmp(rec, "EXTEND", 6) == 0) // key "file does conform ..."
continue;
else if(strncmp(rec, "COMMENT", 7) == 0) // comment of obligatory key in FITS head
continue;
else if(strncmp(rec, "NAXIS", 5) == 0 || strncmp(rec, "BITPIX", 6) == 0) // NAXIS, NAXISxxx, BITPIX
continue;
FITSFUN(fits_write_record, fp, rec);
}
}
FITSFUN(fits_write_record, fp, "COMMENT modified by simple test routine");
TRYFITS(fits_write_img, fp, TDOUBLE, 1, sz, fits->data);
TRYFITS(fits_close_file, fp);
return TRUE;
}

42
_deprecated/FITS/fits.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* fits.h
*
* Copyright 2015 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 __FITS_H__
#define __FITS_H__
#include <stdio.h>
#include <stdbool.h>
typedef struct{
int width; // width
int height; // height
int dtype; // data type
double *data; // picture data
char **keylist; // list of options for each key
size_t keynum; // full number of keys (size of *keylist)
} IMAGE;
void imfree(IMAGE **ima);
bool readFITS(char *filename, IMAGE **fits);
bool writeFITS(char *filename, IMAGE *fits);
#endif // __FITS_H__

44
_deprecated/FITS/main.c Normal file
View File

@@ -0,0 +1,44 @@
/*
* main.c
*
* Copyright 2015 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 <stdio.h>
#include "usefull_macros.h"
#include "fits.h"
void signals(int signo){
exit(signo);
}
int main(int argc, char **argv){
IMAGE *fits;
//size_t i, s;
initial_setup();
if(argc != 3) ERRX("Usage: %s infile outfile", argv[0]);
readFITS(argv[1], &fits);
DBG("ima: %dx%d", fits->width, fits->height);
//s = fits->width * fits->height;
//double *img = fits->data;
//for(i = 0; i < s; ++i) *img++ /= 2.;
unlink(argv[2]);
writeFITS(argv[2], fits);
imfree(&fits);
return 0;
}

View File

@@ -0,0 +1,327 @@
/*
* 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"
/**
* 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;
}
/******************************************************************************\
* 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;
}
static 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");
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
textdomain(GETTEXT_PACKAGE);
#endif
}
/******************************************************************************\
* 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;
}
/**
* 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
\******************************************************************************/
static struct termios oldt, newt; // terminal flags
static int console_changed = 0;
// run on exit:
void restore_console(){
if(console_changed)
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
console_changed = 0;
}
// initial setup:
void setup_con(){
if(console_changed) return;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
WARN(_("Can't setup console"));
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
signals(0); //quit?
}
console_changed = 1;
}
/**
* 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() without need of pressing ENTER
int ret;
do ret = read_console();
while(ret == 0);
return ret;
}
/******************************************************************************\
* TTY with select()
\******************************************************************************/
static struct termio oldtty, tty; // TTY flags
static int comfd = -1; // TTY fd
// run on exit:
void restore_tty(){
if(comfd == -1) return;
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
close(comfd);
comfd = -1;
}
#ifndef BAUD_RATE
#define BAUD_RATE B9600
#endif
// init:
void tty_init(char *comdev){
DBG("\nOpen port...\n");
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
WARN("Can't use port %s\n",comdev);
ioctl(comfd, TCSANOW, &oldtty); // return TTY to previous state
close(comfd);
signals(0); // quit?
}
DBG(" OK\nGet current settings... ");
if(ioctl(comfd,TCGETA,&oldtty) < 0){ // Get settings
WARN(_("Can't get settings"));
signals(0);
}
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){
WARN(_("Can't set settings"));
signals(0);
}
DBG(" 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;
}
int write_tty(uint8_t *buff, size_t length){
ssize_t L = write(comfd, buff, length);
if((size_t)L != length){
WARN("Write error!");
return 1;
}
return 0;
}

View File

@@ -0,0 +1,124 @@
/*
* 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>
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
/*
* GETTEXT
*/
#include <libintl.h>
#define _(String) gettext(String)
#define gettext_noop(String) String
#define N_(String) gettext_noop(String)
#else
#define _(String) (String)
#define N_(String) (String)
#endif
#include <stdlib.h>
#include <termios.h>
#include <termio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdint.h>
// 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;
extern void signals(int sig);
#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); signals(0);}while(0)
#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); signals(0);}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)
double dtime();
// 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);
void restore_console();
void setup_con();
int read_console();
int mygetchar();
void restore_tty();
void tty_init(char *comdev);
size_t read_tty(uint8_t *buff, size_t length);
int write_tty(uint8_t *buff, size_t length);
#endif // __USEFULL_MACROS_H__

View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 3 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, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -0,0 +1,45 @@
# run `make DEF=...` to add extra defines
PROGRAM := opengl
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros -lflycapture-c -lflycapture -L/usr/local/lib
LDFLAGS += -lm -pthread -lglut -lGL -lX11 -lGLEW
SRCS := $(wildcard *.c)
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
OBJDIR := mk
CFLAGS += -O2 -Wno-trampolines -std=gnu99
CFLAGS += -I/usr/local/include/flycapture
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
DEPS := $(OBJS:.o=.d)
CC = gcc
#CXX = g++
all : $(OBJDIR) $(PROGRAM)
debug: CFLAGS += -DEBUG -Werror -Wall -Wextra
debug: all
$(PROGRAM) : $(OBJS)
@echo -e "\t\tLD $(PROGRAM)"
$(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM)
$(OBJDIR):
mkdir $(OBJDIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
clean:
@echo -e "\t\tCLEAN"
@rm -f $(OBJS) $(DEPS)
@rmdir $(OBJDIR) 2>/dev/null || true
xclean: clean
@rm -f $(PROGRAM)
.PHONY: clean xclean

View File

@@ -0,0 +1 @@
imagewiew with GLUT

View File

@@ -0,0 +1,4 @@
Simplest tool for Grasshopper3 control
======================================
(pre-pre-alpha version)

View File

@@ -0,0 +1,58 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/limits.h> // PATH_MAX
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "aux.h"
#include "cmdlnopts.h"
// print messages for given verbose_level
int verbose(verblevel levl, const char *fmt, ...){
if((unsigned)verbose_level < levl) return 0;
va_list ar; int i;
va_start(ar, fmt);
i = vprintf(fmt, ar);
va_end(ar);
printf("\n");
fflush(stdout);
return i;
}
/**
* @brief check_filename - find file name "outfile_xxxx.suff" NOT THREAD-SAFE!
* @param outfile - file name prefix
* @param suff - file name suffix
* @return NULL or next free file name like "outfile_0010.suff" (don't free() it!)
*/
char *check_filename(char *outfile, char *suff){
static char buff[PATH_MAX];
struct stat filestat;
int num;
for(num = 1; num < 10000; num++){
if(snprintf(buff, PATH_MAX, "%s_%04d.%s", outfile, num, suff) < 1)
return NULL;
if(stat(buff, &filestat)) // OK, file not exists
return buff;
}
return NULL;
}

View File

@@ -0,0 +1,35 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef AUX_H__
#define AUX_H__
typedef enum{
VERB_NONE,
VERB_MESG,
VERB_DEBUG
} verblevel;
int verbose(verblevel levl, const char *fmt, ...);
char *check_filename(char *outfile, char *suff);
#define VMESG(...) do{verbose(VERB_MESG, __VA_ARGS__);}while(0)
#define VDBG(...) do{verbose(VERB_DEBUG, __VA_ARGS__);}while(0)
#endif // AUX_H__

View File

@@ -0,0 +1,97 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
static int help;
/*
* here are global parameters initialisation
*/
int verbose_level;
glob_pars G;
// default PID filename:
#define DEFAULT_PIDFILE "/tmp/opengl.pid"
// DEFAULTS
// default global parameters
static glob_pars const Gdefault = {
.device = NULL,
.pidfile = DEFAULT_PIDFILE,
.exptime = NAN,
.gain = NAN
};
/*
* Define command line options by filling structure:
* name has_arg flag val type argptr help
*/
static myoption cmdlnopts[] = {
// common options
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("camera device name")},
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&verbose_level), _("verbose level (each 'v' increases it)")},
{"camno", NEED_ARG, NULL, 'n', arg_int, APTR(&G.camno), _("camera number (if many connected)")},
{"exptime", NEED_ARG, NULL, 'x', arg_float, APTR(&G.exptime), _("exposure time (ms)")},
{"gain", NEED_ARG, NULL, 'g', arg_float, APTR(&G.gain), _("gain value (dB)")},
{"display", NO_ARGS, NULL, 'D', arg_int, APTR(&G.showimage), _("display captured image")},
{"nimages", NEED_ARG, NULL, 'N', arg_int, APTR(&G.nimages), _("number of images to capture")},
{"png", NO_ARGS, NULL, 'p', arg_int, APTR(&G.save_png), _("save png too")},
end_option
};
/**
* Parse 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 *parse_args(int argc, char **argv){
int i;
void *ptr;
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
size_t hlen = 1024;
char helpstring[1024], *hptr = helpstring;
snprintf(hptr, hlen, "Usage: %%s [args] [filename prefix]\n\n\tWhere args are:\n");
// format of help: "Usage: progname [args]\n"
change_helpstring(helpstring);
// parse arguments
parseargs(&argc, &argv, cmdlnopts);
if(help) showhelp(-1, cmdlnopts);
if(argc > 0){
G.rest_pars_num = argc;
G.rest_pars = MALLOC(char *, argc);
for (i = 0; i < argc; i++){
DBG("Found free parameter %s", argv[i]);
G.rest_pars[i] = strdup(argv[i]);
}
}
return &G;
}

View File

@@ -0,0 +1,45 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef CMDLNOPTS_H__
#define CMDLNOPTS_H__
/*
* here are some typedef's for global data
*/
typedef struct{
char *device; // camera device name
char *pidfile; // name of PID file
int camno; // number of camera to work with
float exptime; // exposition time
float gain; // gain value
int showimage; // display last captured image in OpenGL screen
int nimages; // number of images to capture
int save_png; // save png file
int rest_pars_num; // number of rest parameters
char** rest_pars; // the rest parameters: array of char*
} glob_pars;
glob_pars *parse_args(int argc, char **argv);
extern glob_pars G;
extern int verbose_level;
#endif // CMDLNOPTS_H__

View File

@@ -0,0 +1,204 @@
/*
* events.c
*
* Copyright 2015 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 <GL/glew.h>
#include <GL/glut.h>
#include <GL/glext.h>
#include <GL/freeglut.h>
#include <usefull_macros.h>
#include "events.h"
#include "imageview.h"
/**
* manage pressed keys & menu items
*/
static void processKeybrd(unsigned char key, int mod, _U_ int x, _U_ int y){
windowData *win = getWin();
if(!win) return;
DBG("key=%d (%c), mod=%d", key, key, mod);
if(mod == GLUT_ACTIVE_CTRL){ // 'a' == 1, 'b' == 2...
key += 'a'-1;
DBG("CTRL+%c", key);
switch(key){
case 'r': // roll colorfun
win->winevt |= WINEVT_ROLLCOLORFUN;
break;
case 's': // save image
win->winevt |= WINEVT_SAVEIMAGE;
break;
case 'q': // exit case 17:
//signals(1);
win->killthread = 1;
break;
}
}else if(mod == GLUT_ACTIVE_ALT){
; // ALT
}else switch(key){
case '0': // return zoom to 1 & image to 0
win->zoom = 1;
win->x = 0; win->y = 0;
break;
case 27: // esc - kill
win->killthread = 1;
break;
case 'c': // capture in pause mode
DBG("winevt = %d", win->winevt);
if(win->winevt & WINEVT_PAUSE)
win->winevt |= WINEVT_GETIMAGE;
break;
case 'l': // flip left-right
win->flip ^= WIN_FLIP_LR;
break;
case 'p': // pause capturing
win->winevt ^= WINEVT_PAUSE;
break;
case 'u': // flip up-down
win->flip ^= WIN_FLIP_UD;
break;
case 'Z': // zoom+
win->zoom *= 1.1f;
calc_win_props(NULL, NULL);
break;
case 'z': // zoom-
win->zoom /= 1.1f;
calc_win_props(NULL, NULL);
break;
}
}
/*
* Process keyboard
*/
void keyPressed(unsigned char key, int x, int y){
int mod = glutGetModifiers();
//mod: GLUT_ACTIVE_SHIFT, GLUT_ACTIVE_CTRL, GLUT_ACTIVE_ALT; result is their sum
DBG("Key pressed. mod=%d, keycode=%d (%c), point=(%d,%d)\n", mod, key, key, x,y);
processKeybrd(key, mod, x, y);
}
/*
void keySpPressed(_U_ int key, _U_ int x, _U_ int y){
// int mod = glutGetModifiers();
DBG("Sp. key pressed. mod=%d, keycode=%d, point=(%d,%d)\n", glutGetModifiers(), key, x,y);
}*/
static int oldx, oldy; // coordinates when mouse was pressed
static int movingwin = 0; // ==1 when user moves image by middle button
void mousePressed(int key, int state, int x, int y){
// key: GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON
// state: GLUT_UP, GLUT_DOWN
int mod = glutGetModifiers();
windowData *win = getWin();
if(!win) return;
if(state == GLUT_DOWN){
oldx = x; oldy = y;
float X,Y, Zoom = win->zoom;
conv_mouse_to_image_coords(x,y,&X,&Y,win);
DBG("press in (%d, %d) == (%f, %f) on image; mod == %d", x,y,X,Y, mod);
if(key == GLUT_LEFT_BUTTON){
DBG("win->x=%g, win->y=%g", win->x, win->y);
win->x += Zoom * (win->w/2.f - (float)x);
win->y -= Zoom * (win->h/2.f - (float)y);
}else if(key == GLUT_MIDDLE_BUTTON) movingwin = 1;
else if(key == 3){ // wheel UP
if(mod == 0) win->y += 10.f*win->zoom; // nothing pressed - scroll up
else if(mod == GLUT_ACTIVE_SHIFT) win->x -= 10.f*Zoom; // shift pressed - scroll left
else if(mod == GLUT_ACTIVE_CTRL) win->zoom *= 1.1f; // ctrl+wheel up == zoom+
}else if(key == 4){ // wheel DOWN
if(mod == 0) win->y -= 10.f*win->zoom; // nothing pressed - scroll down
else if(mod == GLUT_ACTIVE_SHIFT) win->x += 10.f*Zoom; // shift pressed - scroll right
else if(mod == GLUT_ACTIVE_CTRL) win->zoom /= 1.1f; // ctrl+wheel down == zoom-
}
calc_win_props(NULL, NULL);
}else{
movingwin = 0;
}
}
void mouseMove(int x, int y){
windowData *win = getWin();
if(!win) return;
if(movingwin){
float X, Y, nx, ny, w2, h2;
float a = win->Daspect;
X = (x - oldx) * a; Y = (y - oldy) * a;
nx = win->x + X;
ny = win->y - Y;
w2 = win->image->w / 2.f * win->zoom;
h2 = win->image->h / 2.f * win->zoom;
if(nx < w2 && nx > -w2)
win->x = nx;
if(ny < h2 && ny > -h2)
win->y = ny;
oldx = x;
oldy = y;
calc_win_props(NULL, NULL);
}
}
void menuEvents(int opt){
DBG("opt: %d, key: %d (%c), mod: %d", opt, opt&0xff, opt&0xff, opt>>8);
// just work as shortcut pressed
processKeybrd((unsigned char)(opt&0xff), opt>>8, 0, 0);
} // GLUT_ACTIVE_CTRL
typedef struct{
char *name; // menu entry name
int symbol; // shortcut symbol + rolled modifier
} menuentry;
#define CTRL_K(key) ((key-'a'+1) | (GLUT_ACTIVE_CTRL<<8))
#define SHIFT_K(key) (key | (GLUT_ACTIVE_SHIFT<<8))
#define ALT_K(key) (key | (GLUT_ACTIVE_ALT<<8))
static const menuentry entries[] = {
{"Capture in pause mode (c)", 'c'},
{"Flip image LR (l)", 'l'},
{"Flip image UD (u)", 'u'},
{"Make a pause/continue (p)", 'p'},
{"Restore zoom (0)", '0'},
{"Roll colorfun (ctrl+r)", CTRL_K('r')},
{"Save image (ctrl+s)", CTRL_K('s')},
{"Close this window (ESC)", 27},
{"Quit (ctrl+q)", CTRL_K('q')},
{NULL, 0}
};
#undef CTRL_K
#undef SHIFT_K
#undef ALT_K
void createMenu(){
FNAME();
windowData *win = getWin();
if(!win) return;
DBG("menu for win ID %d", win->ID);
//glutSetWindow(win->ID);
if(win->menu) glutDestroyMenu(win->menu);
win->menu = glutCreateMenu(menuEvents);
const menuentry *ptr = entries;
while(ptr->name){
glutAddMenuEntry(ptr->name, ptr->symbol);
++ptr;
}
DBG("created menu %d\n", win->menu);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}

View File

@@ -0,0 +1,39 @@
/*
* events.h
*
* Copyright 2015 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 __EVENTS_H__
#define __EVENTS_H__
#include <stdlib.h>
#include <stdio.h>
extern float Z; // ËÏÏÒÄÉÎÁÔÁ Z (zoom)
void keyPressed(unsigned char key, int x, int y);
//void keySpPressed(int key, int x, int y);
void mousePressed(int key, int state, int x, int y);
void mouseMove(int x, int y);
void createMenu();
void menuEvents(int opt);
//void mouseWheel(int button, int dir, int x, int y);
#endif // __EVENTS_H__

View File

@@ -0,0 +1,136 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <fitsio.h>
#include <pthread.h>
#include <stdio.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
#include "image_functions.h"
/**
* Convert gray (unsigned short) into RGB components (GLubyte)
* @argument L - gray level (0..1)
* @argument rgb - rgb array (GLubyte [3])
*/
void gray2rgb(double gray, GLubyte *rgb){
int i = gray * 4.;
double x = (gray - (double)i * .25) * 4.;
GLubyte r = 0, g = 0, b = 0;
//r = g = b = (gray < 1) ? gray * 256 : 255;
switch(i){
case 0:
g = (GLubyte)(255. * x);
b = 255;
break;
case 1:
g = 255;
b = (GLubyte)(255. * (1. - x));
break;
case 2:
r = (GLubyte)(255. * x);
g = 255;
break;
case 3:
r = 255;
g = (GLubyte)(255. * (1. - x));
break;
default:
r = 255;
}
*rgb++ = r;
*rgb++ = g;
*rgb = b;
}
static colorfn_type ft = COLORFN_LINEAR;
// all colorfun's should get argument in [0, 1] and return in [0, 1]
static double linfun(double arg){ return arg; } // bung for PREVIEW_LINEAR
static double logfun(double arg){ return log(1.+arg) / 0.6931472; } // for PREVIEW_LOG [log_2(x+1)]
static double (*colorfun)(double) = linfun; // default function to convert color
colorfn_type get_colorfun(){return ft;}
void change_colorfun(colorfn_type f){
DBG("New colorfn: %d", f);
switch (f){
case COLORFN_LOG:
colorfun = logfun;
ft = COLORFN_LOG;
break;
case COLORFN_SQRT:
colorfun = sqrt;
ft = COLORFN_SQRT;
break;
default: // linear
colorfun = linfun;
ft = COLORFN_LINEAR;
}
}
// cycle switch between palettes
void roll_colorfun(){
FNAME();
colorfn_type t = ++ft;
if(t == COLORFN_MAX) t = COLORFN_LINEAR;
change_colorfun(t);
}
/**
* @brief equalize - hystogram equalization
* @param ori (io) - input/output data
* @param w,h - image width and height
* @return data allocated here
*/
static uint8_t *equalize(uint16_t *ori, int w, int h){
uint8_t *retn = MALLOC(uint8_t, w*h);
double orig_hysto[65536] = {0.}; // original hystogram
uint8_t eq_levls[65536] = {0}; // levels to convert: newpix = eq_levls[oldpix]
int s = w*h;
for(int i = 0; i < s; ++i) ++orig_hysto[ori[i]];
double part = (double)(s + 1)/ 256., N = 0.;
for(int i = 0; i < 65536; ++i){
N += orig_hysto[i];
eq_levls[i] = (uint8_t)(N/part);
//printf("N=%g, orig_hysto=%g, eq_levls[%d]=%d\n", N, orig_hysto[i], i, eq_levls[i]);
}
//exit(1);
for(int i = 0; i < s; ++i){
retn[i] = eq_levls[ori[i]];
}
return retn;
}
void change_displayed_image(windowData *win, IMG *img){
if(!win || !win->image) return;
rawimage *im = win->image;
//DBG("imh=%d, imw=%d, ch=%u, cw=%u", im->h, im->w, img->w, img->h);
pthread_mutex_lock(&win->mutex);
int w = img->w, h = img->h, s = w*h;
uint8_t *newima = equalize(img->data, w, h);
GLubyte *dst = im->rawdata;
for(int i = 0; i < s; ++i, dst += 3){
gray2rgb(colorfun(newima[i] / 256.), dst);
}
FREE(newima);
win->image->changed = 1;
pthread_mutex_unlock(&win->mutex);
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef IMAGE_FUNCTIONS__
#define IMAGE_FUNCTIONS__
#include <GL/glut.h>
#include "imageview.h"
// functions for converting grayscale value into colour
typedef enum{
COLORFN_LINEAR, // linear
COLORFN_LOG, // ln
COLORFN_SQRT, // sqrt
COLORFN_MAX // end of list
} colorfn_type;
typedef struct{
uint16_t *data;
int w;
int h;
} IMG;
void change_displayed_image(windowData *win, IMG *convertedImage);
void gray2rgb(double gray, GLubyte *rgb);
colorfn_type get_colorfun();
void change_colorfun(colorfn_type f);
void roll_colorfun();
#endif // IMAGE_FUNCTIONS__

View File

@@ -0,0 +1,353 @@
// bmpview.c
//
// Copyright 2010 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.
//-lglut
#include <X11/Xlib.h> // XInitThreads();
//#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/glext.h>
#include <GL/freeglut.h>
#include <math.h> // roundf(), log(), sqrt()
#include <pthread.h>
#include <usefull_macros.h>
#include "imageview.h"
static windowData *win = NULL; // main window
static pthread_t GLUTthread; // main GLUT thread
static int initialized = 0; // ==1 if GLUT is initialized; ==0 after clear_GL_context
static void createWindow();
static void RedrawWindow();
static void *Redraw(_U_ void *arg);
static void Resize(int width, int height);
/**
* calculate window properties on creating & resizing
*/
void calc_win_props(GLfloat *Wortho, GLfloat *Hortho){
if(!win || ! win->image) return;
float a, A, w, h, W, H;
float Zoom = win->zoom;
w = (float)win->image->w / 2.f;
h = (float)win->image->h / 2.f;
W = (float)win->w;
H =(float) win->h;
A = W / H;
a = w / h;
if(A > a){ // now W & H are parameters for glOrtho
win->Daspect = (float)h / H * 2.f;
W = h * A; H = h;
}else{
win->Daspect = (float)w / W * 2.f;
H = w / A; W = w;
}
if(Wortho) *Wortho = W;
if(Hortho) *Hortho = H;
// calculate coordinates of center
win->x0 = W/Zoom - w + win->x / Zoom;
win->y0 = H/Zoom + h - win->y / Zoom;
}
/**
* create window & run main loop
*/
static void createWindow(){
FNAME();
DBG("ini=%d, win %s", initialized, win ? "yes" : "no");
if(!initialized) return;
if(!win) return;
int w = win->w, h = win->h;
DBG("create window with title %s", win->title);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(w, h);
win->ID = glutCreateWindow(win->title);
//glewInit();
DBG("created GL_ID=%d", win->ID);
glGenTextures(1, &(win->Tex));
calc_win_props(NULL, NULL);
win->zoom = 1. / win->Daspect;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, win->Tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, win->image->w, win->image->h, 0,
GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glDisable(GL_TEXTURE_2D);
createMenu();
DBG("Window opened");
glutReshapeFunc(Resize);
glutDisplayFunc(RedrawWindow);
glutKeyboardFunc(keyPressed);
//glutSpecialFunc(keySpPressed);
//glutMouseWheelFunc(mouseWheel);
glutMouseFunc(mousePressed);
glutMotionFunc(mouseMove);
//glutIdleFunc(glutPostRedisplay);
glutIdleFunc(RedrawWindow);
//pthread_create(&GLUTthread, NULL, &Redraw, NULL);
}
int killwindow(){
FNAME();
if(!win) return 0;
if(!win->killthread){
// say changed thread to die
win->killthread = 1;
}
pthread_mutex_lock(&win->mutex);
//glutSetWindow(win->ID); // obviously set window (for closing from menu)
DBG("wait for changed thread");
pthread_join(win->thread, NULL); // wait while thread dies
if(win->menu) glutDestroyMenu(win->menu);
glutDestroyWindow(win->ID);
DBG("destroy menu, wundow & texture %d", win->Tex);
glDeleteTextures(1, &(win->Tex));
//glFinish();
windowData *old = win;
win->ID = 0;
win = NULL;
DBG("free(rawdata)");
FREE(old->image->rawdata);
DBG("free(image)");
FREE(old->image);
pthread_mutex_unlock(&old->mutex);
DBG("free(win)");
FREE(old);
DBG("return");
return 1;
}
void renderBitmapString(float x, float y, void *font, char *string, GLubyte *color){
if(!initialized) return;
char *c;
int x1=x, W=0;
for(c = string; *c; c++){
W += glutBitmapWidth(font,*c);// + 1;
}
x1 -= W/2;
glColor3ubv(color);
glLoadIdentity();
glTranslatef(0.,0., -150);
//glTranslatef(x,y, -4000.);
for (c = string; *c != '\0'; c++){
glColor3ubv(color);
glRasterPos2f(x1,y);
glutBitmapCharacter(font, *c);
//glutStrokeCharacter(GLUT_STROKE_ROMAN, *c);
x1 = x1 + glutBitmapWidth(font,*c);// + 1;
}
}
static void RedrawWindow(){
if(!initialized || !win) return;
if(pthread_mutex_trylock(&win->mutex) != 0) return;
GLfloat w = win->image->w, h = win->image->h;
glClearColor(0.0, 0.0, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(win->x, win->y, 0.);
glScalef(-win->zoom, -win->zoom, 1.);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, win->Tex);
if(win->image->changed){
DBG("Changed");
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, win->image->w, win->image->h,
GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata);
win->image->changed = 0;
}
w /= 2.f; h /= 2.f;
float lr = 1., ud = 1.; // flipping coefficients
if(win->flip & WIN_FLIP_LR) lr = -1.;
if(win->flip & WIN_FLIP_UD) ud = -1.;
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 1.0f); glVertex2f( -1.f*lr*w, ud*h ); // top right
glTexCoord2f(1.0f, 0.0f); glVertex2f( -1.f*lr*w, -1.f*ud*h ); // bottom right
glTexCoord2f(0.0f, 0.0f); glVertex2f(lr*w, -1.f*ud*h ); // bottom left
glTexCoord2f(0.0f, 1.0f); glVertex2f(lr*w, ud*h ); // top left
glEnd();
glDisable(GL_TEXTURE_2D);
glFinish();
glutSwapBuffers();
pthread_mutex_unlock(&win->mutex);
usleep(10000);
}
/**
* main freeGLUT loop
* waits for global signals to create windows & make other actions
*/
static void *Redraw(_U_ void *arg){
FNAME();
createWindow();
glutMainLoop();
/* while(1){
if(!initialized){
DBG("!initialized -> exit thread");
return NULL;
}
if(!win || win->killthread){
DBG("Thread killed");
return NULL;
}
if(win && win->ID > 0){
//glutSetWindow(win->ID);
glutPostRedisplay();
//RedrawWindow();
glutMainLoopEvent();
usleep(10000);
}
}*/
return NULL;
}
static void Resize(int width, int height){
if(!initialized) return;
//int window = glutGetWindow();
if(!win/* || !window*/) return;
glutReshapeWindow(width, height);
win->w = width;
win->h = height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat W, H;
calc_win_props(&W, &H);
glOrtho(-W,W, -H,H, -1., 1.);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/**
* create new window, run thread & return pointer to its structure or NULL
* asynchroneous call from outside
* wait for window creating & return its data
* @param title - header (copyed inside this function)
* @param w,h - image size
* @param rawdata - NULL (then the memory will be allocated here with size w x h)
* or allocated outside data
*/
windowData *createGLwin(char *title, int w, int h, rawimage *rawdata){
FNAME();
if(!initialized) return NULL;
if(win) killwindow();
win = MALLOC(windowData, 1);
rawimage *raw;
if(rawdata){
raw = rawdata;
}else{
raw = MALLOC(rawimage, 1);
if(raw){
raw->rawdata = MALLOC(GLubyte, w*h*3);
raw->w = w;
raw->h = h;
raw->changed = 1;
// raw->protected is zero automatically
}
}
if(!raw || !raw->rawdata){
free(raw);
return NULL;
}
win->title = strdup(title);
win->image = raw;
if(pthread_mutex_init(&win->mutex, NULL)){
WARN(_("Can't init mutex!"));
killwindow();
return NULL;
}
win->w = w;
win->h = h;
pthread_create(&GLUTthread, NULL, &Redraw, NULL);
return win;
}
/**
* Init freeGLUT
*/
void imageview_init(){
FNAME();
char *v[] = {"Sample window", NULL};
int c = 1;
static int glutnotinited = 1;
if(initialized){
WARNX(_("Already initialized!"));
return;
}
if(glutnotinited){
DBG("init");
// XInitThreads(); // we need it for threaded windows
glutInit(&c, v);
glutnotinited = 0;
}
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
initialized = 1;
}
/**
* Close all opened windows and terminate main GLUT thread
*/
void clear_GL_context(){
FNAME();
if(!initialized) return;
initialized = 0;
glutLeaveMainLoop();
DBG("kill");
killwindow();
DBG("join");
pthread_join(GLUTthread, NULL); // wait while main thread exits
DBG("main GL thread cancelled");
}
/*
* Coordinates transformation from CS of drawingArea into CS of picture
* x,y - pointer coordinates
* X,Y - coordinates of appropriate point at picture
*/
void conv_mouse_to_image_coords(int x, int y,
float *X, float *Y,
windowData *window){
float a = window->Daspect / window->zoom;
*X = (float)x * a - window->x0;
*Y = window->y0 - (float)y * a;
}
void conv_image_to_mouse_coords(float X, float Y,
int *x, int *y,
windowData *window){
float a = window->zoom / window->Daspect;
*x = (int)roundf((X + window->x0) * a);
*y = (int)roundf((window->y0 - Y) * a);
}
windowData *getWin(){
return win;
}

View File

@@ -0,0 +1,88 @@
/*
* imageview.h
*
* Copyright 2015 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 __BMPVIEW_H__
#define __BMPVIEW_H__
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <string.h>
#include "events.h"
typedef struct{
GLubyte *rawdata; // raw image data
int w; // size of image
int h;
int changed; // == 1 if data was changed outside (to redraw)
} rawimage;
// events from menu:
// temporaly stop capture of regular sequence
#define WINEVT_PAUSE (1<<0)
// capture one image in pause mode
#define WINEVT_GETIMAGE (1<<1)
// save current image
#define WINEVT_SAVEIMAGE (1<<2)
// change color palette function
#define WINEVT_ROLLCOLORFUN (1<<3)
// flip image
#define WIN_FLIP_LR (1<<0)
#define WIN_FLIP_UD (1<<1)
typedef struct{
int ID; // identificator of OpenGL window
char *title; // title of window
GLuint Tex; // texture for image inside window
rawimage *image; // raw image data
int w; int h; // window size
float x; float y; // image offset coordinates
float x0; float y0; // center of window for coords conversion
float zoom; // zoom aspect
float Daspect; // aspect ratio between image & window sizes
int menu; // window menu identifier
uint32_t winevt; // window menu events
uint8_t flip; // flipping settings
pthread_t thread; // identificator of thread that changes window data
pthread_mutex_t mutex;// mutex for operations with image
int killthread; // flag for killing data changing thread & also signal that there's no threads
} windowData;
typedef enum{
INNER,
OPENGL
} winIdType;
void imageview_init();
windowData *createGLwin(char *title, int w, int h, rawimage *rawdata);
windowData *getWin();
int killwindow();
void renderBitmapString(float x, float y, void *font, char *string, GLubyte *color);
void clear_GL_context();
void calc_win_props(GLfloat *Wortho, GLfloat *Hortho);
void conv_mouse_to_image_coords(int x, int y, float *X, float *Y, windowData *window);
void conv_image_to_mouse_coords(float X, float Y, int *x, int *y, windowData *window);
#endif // __BMPVIEW_H__

View File

@@ -0,0 +1,162 @@
/*
* This file is part of the opengl project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "aux.h"
#include "cmdlnopts.h"
#include "image_functions.h"
#include "imageview.h"
void signals(int sig){
if(sig){
signal(sig, SIG_IGN);
DBG("Get signal %d, quit.\n", sig);
}
putlog("Exit with status %d", sig);
if(G.pidfile) // remove unnesessary PID file
unlink(G.pidfile);
exit(sig);
}
// manage some menu/shortcut events
static void winevt_manage(windowData *win, IMG *convertedImage){
if(win->winevt & WINEVT_SAVEIMAGE){ // save image
VDBG("Try to make screenshot");
DBG("Try to make screenshot");
//saveImages(convertedImage, "ScreenShot");
win->winevt &= ~WINEVT_SAVEIMAGE;
}
if(win->winevt & WINEVT_ROLLCOLORFUN){
roll_colorfun();
win->winevt &= ~WINEVT_ROLLCOLORFUN;
change_displayed_image(win, convertedImage);
}
}
// main thread to deal with image
static void* image_thread(_U_ void *data){
FNAME();
IMG *img = (IMG*) data;
while(1){
windowData *win = getWin();
if(!win){
DBG("!win");
pthread_exit(NULL);
}
if(win->killthread){
DBG("got killthread");
pthread_exit(NULL);
}
if(win->winevt) winevt_manage(win, img);
usleep(10000);
}
}
static void modifyImg(IMG* ima){
for(int x = 0; x < ima->w; ++x){
uint16_t c = rand() & 0xffff;
int y = rand() % ima->h;
ima->data[x + y*ima->w] = c;
ima->data[5 + x*ima->w] = 1000;
ima->data[105 + x*ima->w] = 10000;
ima->data[205 + x*ima->w] = 50000;
ima->data[305 + x*ima->w] = 0xffff;
}
}
int main(int argc, char **argv){
int ret = 0;
initial_setup();
char *self = strdup(argv[0]);
parse_args(argc, argv);
char *outfprefix = NULL;
if(G.rest_pars_num){
if(G.rest_pars_num != 1){
WARNX("You should point only one free argument - filename prefix");
signals(1);
}else outfprefix = G.rest_pars[0];
}
check4running(self, G.pidfile);
FREE(self);
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
windowData *mainwin = NULL;
if(!G.showimage && !outfprefix){ // not display image & not save it?
ERRX("You should point file name or option `display image`");
}
if(G.showimage){
imageview_init();
}
IMG convertedImage = {.h = 400, .w = 400};
convertedImage.data = MALLOC(uint16_t, convertedImage.h * convertedImage.w);
for(int i = 0; i < convertedImage.h * convertedImage.w; ++i) convertedImage.data[i] = 100;
DBG("data[200*200]=%d", convertedImage.data[200*200]);
if(G.showimage){
if(!mainwin){
DBG("Create window @ start");
mainwin = createGLwin("Sample window", convertedImage.w, convertedImage.h, NULL);
if(!mainwin){
WARNX("Can't open OpenGL window, image preview will be inaccessible");
}else
pthread_create(&mainwin->thread, NULL, &image_thread, (void*)&convertedImage); //(void*)mainwin);
}
if((mainwin = getWin())){
DBG("change image");
if(mainwin->killthread) goto destr;
modifyImg(&convertedImage);
change_displayed_image(mainwin, &convertedImage);
while((mainwin = getWin())){ // test paused state & grabbing custom frames
if((mainwin->winevt & WINEVT_PAUSE) == 0) break;
if(mainwin->winevt & WINEVT_GETIMAGE){
mainwin->winevt &= ~WINEVT_GETIMAGE;
modifyImg(&convertedImage);
change_displayed_image(mainwin, &convertedImage);
}
usleep(10000);
}
}
}
if((mainwin = getWin())) mainwin->winevt |= WINEVT_PAUSE;
destr:
if(G.showimage){
while((mainwin = getWin())){
modifyImg(&convertedImage);
change_displayed_image(mainwin, &convertedImage);
if(mainwin->killthread) break;
if(mainwin->winevt & WINEVT_GETIMAGE){
mainwin->winevt &= ~WINEVT_GETIMAGE;
modifyImg(&convertedImage);
change_displayed_image(mainwin, &convertedImage);
}
}
DBG("Close window");
clear_GL_context();
}
signals(ret);
return ret;
}

View File

@@ -0,0 +1 @@
-std=c17

View File

@@ -0,0 +1,2 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42

View File

@@ -0,0 +1 @@
[General]

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.3, 2020-12-07T13:16:57. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">2</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">4</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/g</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">true</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.10.1, 2020-03-30T18:57:01. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Big/Data/Cameras/Grasshopper/My/src</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Конфигурация развёртывания</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Особая программа</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -0,0 +1 @@
-std=c++17

View File

@@ -0,0 +1,12 @@
aux.c
aux.h
camera_functions.h
cmdlnopts.c
cmdlnopts.h
events.c
events.h
opengl.c
image_functions.c
image_functions.h
imageview.c
imageview.h

View File

@@ -0,0 +1,2 @@
.
/usr/local/include/flycapture

29
_deprecated/GPS/Makefile Normal file
View File

@@ -0,0 +1,29 @@
PROGRAM = gpstest
LDFLAGS = -lm
SRCS = $(wildcard *.c)
OBJDIR = mk
CC = gcc
DEFINES = -D_XOPEN_SOURCE=1001
CFLAGS = -DEBUG -Wall -Werror -Wextra $(DEFINES)
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
all : $(OBJDIR) $(OBJS) $(PROGRAM)
$(PROGRAM) : $(OBJDIR) $(OBJS)
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
$(OBJDIR):
mkdir $(OBJDIR)
$(OBJDIR)/%.o: %.c
@printf " CC $<\n"
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ -c $<
# some addition dependencies
#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS)
# @touch $@
clean:
/bin/rm -rf $(OBJDIR)
depend:
$(CXX) -MM $(CXX.SRCS)

1
_deprecated/GPS/README Normal file
View File

@@ -0,0 +1 @@
Small utility for working with GPS module NEO-6M

View File

@@ -0,0 +1,93 @@
/*
* 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"
#include "usefull_macros.h"
/*
* here are global parameters initialisation
*/
glob_pars G; // internal global parameters structure
int help = 0; // whether to show help string
glob_pars Gdefault = {
.devpath = "/dev/ttyUSB0"
,.pollubx = 0
,.pollinterval = 1.
,.block_msg = {0,1,0,0,0,0} // all exept RMC are blocked by default
,.polltmout = 10.
,.stationary = 0
,.gettimediff = 0
,.meancoords = 0
,.silent = 0
,.date = 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")},
/// "ÐÕÔØ Ë ÕÓÔÒÏÊÓÔ×Õ"
{"device",1, NULL, 'd', arg_string, APTR(&G.devpath), N_("device path")},
{"poll-udx", 0, NULL, 'p', arg_none, APTR(&G.pollubx), N_("poll UDX,00")},
{"pollinterval",1,NULL, 'i', arg_double, APTR(&G.pollinterval),N_("polling interval")},
{"no-rmc",0,&G.block_msg[GPRMC], 0, arg_none, NULL, N_("block RMC message")},
{"gsv", 0, &G.block_msg[GPGSV], 1, arg_none, NULL, N_("process GSV message")},
{"gsa", 0, &G.block_msg[GPGSA], 1, arg_none, NULL, N_("process GSA message")},
{"gga", 0, &G.block_msg[GPGGA], 1, arg_none, NULL, N_("process GGA message")},
{"gll", 0, &G.block_msg[GPGLL], 1, arg_none, NULL, N_("process GLL message")},
{"vtg", 0, &G.block_msg[GPVTG], 1, arg_none, NULL, N_("process VTG message")},
{"timeout", 1, NULL, 't', arg_double, APTR(&G.polltmout), N_("polling timeout")},
{"stationary",0, NULL, 's', arg_int, APTR(&G.stationary),N_("configure as stationary")},
{"timediff",0, NULL, 'T', arg_int, APTR(&G.gettimediff),N_("calculate mean time difference")},
{"coords",0, NULL, 'C', arg_int, APTR(&G.meancoords),N_("calculate mean coordinates")},
{"silent",0, NULL, 'S', arg_int, APTR(&G.silent), N_("don't write intermediate messages")},
{"print-date",0,NULL, 'D', arg_int, APTR(&G.date), N_("print date in format MMDDhhmmCCYY.ss")},
// ...
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;
memcpy(&G, &Gdefault, sizeof(G));
/// "éÓÐÏÌØÚÏ×ÁÎÉÅ: %s [ÁÒÇÕÍÅÎÔÙ]\n\n\tçÄÅ ÁÒÇÕÍÅÎÔÙ:\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("\n%s\n", _("Ignore argument[s]:"));
for (i = 0; i < argc; i++)
printf("\t%s\n", argv[i]);
}
return &G;
}

View File

@@ -0,0 +1,58 @@
/*
* 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"
// GSV, RMC, GSA, GGA, GLL, VTG, TXT
extern char *GPmsgs[];
typedef enum{
GPGSV = 0,
GPRMC,
GPGSA,
GPGGA,
GPGLL,
GPVTG,
GPMAXMSG
}GPmsg_type;
/*
* here are some typedef's for global data
*/
typedef struct{
char *devpath; // device path
int pollubx; // flag of polling ubx00
double pollinterval; // ubx00 polling interval (in seconds)
int block_msg[GPMAXMSG]; // array of messages: 1-show, 0-block
double polltmout; // polling timeout (program ends after this interval)
int stationary; // configure as stationary
int gettimediff; // calculate mean time difference
int meancoords; // calculate mean coordinates
int silent; // don't write intermediate messages
int date; // print date for initial time setup
}glob_pars;
glob_pars *parce_args(int argc, char **argv);
#endif // __CMDLNOPTS_H__

140
_deprecated/GPS/daemon.c Normal file
View File

@@ -0,0 +1,140 @@
/*
* daemon.c - functions for running in background like a daemon
*
* 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 "daemon.h"
#include <stdio.h> // printf, fopen, ...
#include <unistd.h> // getpid
#include <stdio.h> // perror
#include <sys/types.h> // opendir
#include <dirent.h> // opendir
#include <sys/stat.h> // stat
#include <fcntl.h> // fcntl
#include <stdlib.h> // exit
#include <string.h> // memset
/**
* read process name from /proc/PID/cmdline
* @param pid - PID of interesting process
* @return filename or NULL if not found
* don't use this function twice for different names without copying
* its returning by strdup, because `name` contains in static array
*/
char *readname(pid_t pid){
static char name[256];
char *pp = name, byte, path[256];
FILE *file;
int cntr = 0;
size_t sz;
snprintf (path, 255, PROC_BASE "/%d/cmdline", pid);
file = fopen(path, "r");
if(!file) return NULL; // there's no such file
do{ // read basename
sz = fread(&byte, 1, 1, file);
if(sz != 1) break;
if(byte != '/') *pp++ = byte;
else{
pp = name;
cntr = 0;
}
}while(byte && cntr++ < 255);
name[cntr] = 0;
fclose(file);
return name;
}
void iffound_default(pid_t pid){
fprintf(stderr, "\nFound running process (pid=%d), exit.\n", pid);
exit(0);
}
/**
* check wether there is a same running process
* exit if there is a running process or error
* Checking have 3 steps:
* 1) lock executable file
* 2) check pidfile (if you run a copy?)
* 3) check /proc for executables with the same name (no/wrong pidfile)
* @param argv - argument of main() or NULL for non-locking, call this function before getopt()
* @param pidfilename - name of pidfile or NULL if none
* @param iffound - action to run if file found or NULL for exit(0)
*/
void check4running(char **argv, char *pidfilename, void (*iffound)(pid_t pid)){
DIR *dir;
FILE *pidfile, *fself;
struct dirent *de;
struct stat s_buf;
pid_t pid = 0, self;
struct flock fl;
char *name, *myname;
if(!iffound) iffound = iffound_default;
if(argv){ // block self
fself = fopen(argv[0], "r"); // open self binary to lock
memset(&fl, 0, sizeof(struct flock));
fl.l_type = F_WRLCK;
if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking
perror("fcntl");
exit(1);
}
if(fl.l_type != F_UNLCK){ // file is locking - exit
printf("Found locker, PID = %d!\n", fl.l_pid);
exit(1);
}
fl.l_type = F_RDLCK;
if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){
perror("fcntl");
exit(1);
}
}
self = getpid(); // get self PID
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
perror(PROC_BASE);
exit(1);
}
if(!(name = readname(self))){ // error reading self name
perror("Can't read self name");
exit(1);
}
myname = strdup(name);
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
pidfile = fopen(pidfilename, "r");
if(pidfile){
if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
iffound(pid);
}
fclose(pidfile);
}
}
// There is no pidfile or it consists a wrong record
while((de = readdir(dir))){ // scan /proc
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
continue;
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
iffound(pid);
}
closedir(dir);
if(pidfilename){
pidfile = fopen(pidfilename, "w");
fprintf(pidfile, "%d\n", self); // write self PID to pidfile
fclose(pidfile);
}
free(myname);
}

28
_deprecated/GPS/daemon.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* daemon.h
*
* Copyright 2015 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.
*/
#ifndef PROC_BASE
#define PROC_BASE "/proc"
#endif
#include <unistd.h> // pid_t
void iffound_default(pid_t pid);
void check4running(char **argv, char *pidfilename, void (*iffound)(pid_t pid));

685
_deprecated/GPS/main.c Normal file
View File

@@ -0,0 +1,685 @@
/*
* main.c
*
* Copyright 2015 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 "usefull_macros.h"
#include "cmdlnopts.h"
#include "daemon.h"
#include <assert.h>
#include <signal.h>
#include <time.h>
#include <math.h> // sqrt
#ifndef PIDFILE
#define PIDFILE "/tmp/GPStest.pid"
#endif
glob_pars *GP = NULL;
#define PRINT(...) do{if(!GP->silent) printf(__VA_ARGS__);}while(0)
// Messages for blocking: GSV, RMC, GSA, GGA, GLL, VTG
char *GPmsgs[] = {"GSV", "RMC", "GSA", "GGA", "GLL", "VTG"};
static void signals(int sig){
DBG("Get signal %d, quit.\n", sig);
unlink(PIDFILE);
restore_tty();
exit(sig);
}
uint8_t *get_portdata(){
static uint8_t buf[1025];
uint8_t *ptr = buf;
size_t L = 0, rest = 1024;
while(rest && (L = read_tty(ptr, rest))){
rest -= L;
ptr += L;
}
if(ptr != buf){
*ptr = 0;
ptr = buf;
}else ptr = NULL;
return ptr;
}
/**
* Calculate checksum & write message to port
* @param buf - command to write (with leading $ and trailing *)
* return 0 if fails
*/
int write_with_checksum(uint8_t *buf){
static char CS[3];
//uint8_t *ptr = buf;
uint8_t checksum = 0;
if(*buf != '$') return 0;
if(write_tty(buf, strlen((char*)buf))) return 0;
++buf; // skip leaders
do{
checksum ^= *buf++;
}while(*buf && *buf != '*');
snprintf(CS, 3, "%X", checksum);
if(write_tty((uint8_t*)CS, 2)) return 0;
if(write_tty((uint8_t*)"\r\n", 2)) return 0;
//DBG("Write: %s%c%c", ptr, CS[0], CS[1]);
return 1;
}
// Check checksum
int checksum(uint8_t *buf){
uint8_t *eol;
char chs[3];
uint8_t checksum = 0;
if(*buf != '$' || !(eol = (uint8_t*)strchr((char*)buf, '*'))){
DBG("Wrong data: %s\n", buf);
return 0;
}
while(++buf != eol)
checksum ^= *buf;
snprintf(chs, 3, "%02X", checksum);
if(strncmp(chs, (char*)++buf, 2)){
DBG("Wrong checksum: %s", chs);
return 0;
}
return 1;
}
uint8_t *nextpos(uint8_t **buf, int pos){
int i;
if(pos < 1) pos = 1;
for(i = 0; i < pos; ++i){
*buf = (uint8_t*)strchr((char*)*buf, ',');
if(!*buf) break;
++(*buf);
}
return *buf;
}
#define NEXT() do{if(!nextpos(&buf, 1)) goto ret;}while(0)
#define SKIP(NPOS) do{if(!nextpos(&buf, NPOS)) goto ret;}while(0)
double timediff_aver = 0.;
int timediff_N = 0;
/**
* difference (in seconds) in system & GPS clock
* @return system_time - GPS_time
*/
double timediff(int h, int m, double s){
struct timeval tv;
// struct timezone tz;
gettimeofday(&tv, NULL);
time_t tm0 = time(NULL);
struct tm *gmt = gmtime(&tm0);
double td = (gmt->tm_hour - h) * 3600.;
td += (gmt->tm_min - m) * 60.;
td += ((double)tv.tv_usec)/1e6 + (gmt->tm_sec - s);
timediff_aver += td;
++timediff_N;
return td;
}
double Latt_mean = 0., Long_mean = 0., Latt_sq = 0., Long_sq = 0.;
int Latt_N = 0, Long_N = 0;
/**
* acquire last command
* @return 0 if failed
*/
int get_ACK(uint8_t *conf) {
int i;
uint8_t ack[10] = {0xb5, 0x62, // header
0x05, 0x01, // ACK-ACK
2, 0}; // 2 bytes, little-endian
// send ACK to recent CONF changes
ack[6] = conf[2];
ack[7] = conf[3];
double T0 = dtime();
// checksum
for (i = 2; i < 8; ++i){
ack[8] += ack[i];
ack[9] += ack[8];
}
/*
printf("ack: ");
for(i = 0; i < 10; ++i)
printf("%X ", ack[i]);
printf("\n");
*/
uint8_t buf[10], *ptr = buf, *cmp = ack;
size_t remain = 10;
// wait not more than 3 seconds
while(dtime() - T0 < 3. && remain){
size_t L = read_tty(ptr, remain);
if(L){
remain -= L;
for(i = 0; i < (int)L; ++i){
//DBG("got: %X (%c)", *ptr, *ptr);
if(*ptr++ != *cmp++){
//DBG("NEQ: %X != %X", ptr[-1], cmp[-1]);
ptr = buf; cmp = ack; remain = 10;
break;
}
}
}
}
if(!remain) return 1;
return 0;
}
void cfg_stationary(){
int i;
uint8_t stat[44] = {0xb5, 0x62, // header
0x06, 0x24, // CFG-NAV5
36, 0, // 36 bytes, little-endian
1, 0, // mask: only dynamic model
2}; // stationary model
// stat[44] = '\r';
// stat[45] = '\n';
/*
uint8_t stat[] = {
0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x02, 0x03,
0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00,
0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, '\r', '\n'};
*/
for(i = 2; i < 42; ++i){
stat[42] += stat[i];
stat[43] += stat[42];
}
/*
printf("conf: ");
for(i = 0; i < 44; ++i)
printf("%X ", conf[i]);
printf("\n");
*/
i = 0;
do{
write_tty(stat, 44);
DBG("Written, aquire");
}while(!get_ACK(stat) && ++i < 11);
// precise point position
uint8_t prec[48] = {0xb5, 0x62, // header
0x06, 0x23, // CFG-NAVX5
40, 0, // 40 bytes, little-endian
0, 0, 0, 0x20}; // mask for PPP
prec[32] = 1; // usePPP = TRUE (field 26)
for(i = 2; i < 46; ++i){
prec[46] += prec[i];
prec[47] += prec[46];
}
i = 0;
do{
write_tty(prec, 48);
DBG("Written, aquire");
}while(!get_ACK(prec) && ++i < 11);
}
/**
* Show current date in appropriate format for initial clock setup (MMDDhhmmCCYY.ss)
* make from root:
* date $(gpstest -sSD)
*/
void show_date(int H, int M, double S, int d, int m, int y){
printf("--utc %02d%02d%02d%02d%02d.%02d\n", m, d, H, M, y, (int)(S + 0.3));
signals(0); // appropriate exit
}
/*
* Satellites in View
* $GPGSV,NoMsg,MsgNo,NoSv,{,sv,elv,az,cno}*cs
* 1 = total number of GPGSV messages being output
* 2 = Number of this message
* 3 = Satellites in View
* 4-7 will be repeated 1..4 times
* sv = Satellite ID
* elv = Elevation, range 0..90deg
* az = Azimuth, range 0..359deg
* cno = SNR, range 0.99dB
* cs = control sum
* 3,1,11,01,68,278,,03,39,292,08,04,66,190,30,11,55,231,34*79
* 3,2,11,14,30,050,10,17,11,322,,19,09,202,,22,14,096,*74
* 3,3,11,23,20,232,30,31,43,118,33,32,76,352,*43
*/
void gsv(uint8_t *buf){
//DBG("gsv: %s\n", buf);
int Nrec = -1, Ncur, inview = 0;
static int sat_scanned = 0;
if(sscanf((char*)buf, "%d,%d,", &Nrec, &Ncur) != 2) goto ret;
SKIP(2);
if(sscanf((char*)buf, "%d,", &inview) != 1) goto ret;
if(inview < 1) goto ret;
NEXT();
if(Ncur == 1)
PRINT("%d satellites in view field: (No: ID, elevation, azimuth, SNR)\n", inview);
do{
int id, el, az, snr;
// there could be no "SNR" field if we can't find this satellite on sky
if(sscanf((char*)buf, "%d,%d,%d,%d,", &id, &el, &az, &snr) < 3) break;
PRINT(" (%d: %d, %d, %d, %d)", ++sat_scanned, id, el, az, snr);
SKIP(4);
}while(1);
ret:
if(inview < 1) PRINT("There's no GPS satellites in viewfield\n");
if(Nrec > 0 && Nrec == Ncur){
sat_scanned = 0;
PRINT("\n");
}
}
/*
* Recommended minimum specific GPS/Transit data
* $GPRMC,hhmmss,status,latitude,N,longitude,E,spd,cog,ddmmyy,mv,mvE,mode*cs
* 1 = UTC of position fix
* 2 = Data status (V=navigation receiver warning)
* 3 = Latitude of fix
* 4 = N or S
* 5 = Longitude of fix
* 6 = E or W
* 7 = Speed over ground in knots
* 8 = Cource over ground in degrees
* 9 = UT date
* 10 = Magnetic variation degrees (Easterly var. subtracts from true course)
* 11 = E or W
* 12 = Mode: N(bad), E(approx), A(auto), D(diff)
* 213457.00,A,4340.59415,N,04127.47560,E,2.494,,290615,,,A*7B
*/
void rmc(uint8_t *buf){
//DBG("rmc: %s\n", buf);
int H, M, LO, LA, d, m, y, getdate = 0;
double S, longitude, lattitude, speed, track, mag;
char varn = 'V', north = '0', east = '0', mageast = '0', mode = 'N';
sscanf((char*)buf, "%2d%2d%lf", &H, &M, &S);
NEXT();
if(*buf != ',') varn = *buf;
if(varn != 'A')
PRINT("(data could be wrong)");
else{
PRINT("(data valid)");
if(GP->date) getdate = 1; // as only we have valid data we show it to user
}
PRINT(" time: %02d:%02d:%05.2f", H, M, S);
PRINT(" timediff: %g", timediff(H, M, S));
NEXT();
sscanf((char*)buf, "%2d%lf", &LA, &lattitude);
NEXT();
if(*buf != ','){
north = *buf;
lattitude = (double)LA + lattitude / 60.;
if(north == 'S') lattitude = -lattitude;
PRINT(" latt: %g", lattitude);
Latt_mean += lattitude;
Latt_sq += lattitude*lattitude;
++Latt_N;
}
NEXT();
sscanf((char*)buf, "%3d%lf", &LO, &longitude);
NEXT();
if(*buf != ','){
east = *buf;
longitude = (double)LO + longitude / 60.;
if(east == 'W') longitude = -longitude;
PRINT(" long: %g", longitude);
Long_mean += longitude;
Long_sq += longitude*longitude;
++Long_N;
}
NEXT();
if(*buf != ','){
sscanf((char*)buf, "%lf", &speed);
PRINT(" speed: %gknots", speed);
}
NEXT();
if(*buf != ','){
sscanf((char*)buf, "%lf", &track);
PRINT(" track: %gdeg,True", track);
}
NEXT();
if(sscanf((char*)buf, "%2d%2d%2d", &d, &m, &y) == 3)
PRINT(" date(dd/mm/yy): %02d/%02d/%02d", d, m, y);
if(getdate) show_date(H,M,S,d,m,y); // show date & exit
NEXT();
sscanf((char*)buf, "%lf,%c", &mag, &mageast);
if(mageast == 'E' || mageast == 'W'){
if(mageast == 'W') mag = -mag;
PRINT(" magnetic var: %g", mag);
}
SKIP(2);
if(*buf != ','){
mode = *buf;
PRINT(" mode: %c", mode);
}
ret:
PRINT("\n");
}
/*
* Overall Satellite data
* $GPGSA,Smode,FS{,sv},PDOP,HDOP,VDOP*cs
* 1 = mode: 'M' - manual, 'A' - auto
* 2 = fix status: 1 - not available, 2 - 2D, 3 - 3D
* 3..14 = used satellite number
* 15 = position dilution
* 16 = horizontal dilution
* 17 = vertical dilution
* A,2,04,31,32,14,19,,,,,,,,2.77,2.58,1.00*05
*/
void gsa(uint8_t *buf){
//DBG("gsa: %s\n", buf);
int used[12];
int i, Nused = 0;
if(*buf == 'M') PRINT("Mode: manual; ");
else if(*buf == 'A') PRINT("Mode: auto; ");
else return; // wrong data
NEXT();
switch(*buf){
case '1':
PRINT("no fix; ");
break;
case '2':
PRINT("2D fix; ");
break;
case '3':
PRINT("3D fix; ");
break;
default:
goto ret;
}
NEXT();
for(i = 0; i < 12; ++i){
int N;
if(sscanf((char*)buf, "%d,", &N) == 1)
used[Nused++] = N;
NEXT();
}
if(Nused){
PRINT("%d satellites used: ", Nused);
for(i = 0; i < Nused; ++i)
PRINT("%d, ", used[i]);
}
double pos, hor, vert;
if(sscanf((char*)buf, "%lf,%lf,%lf", &pos, &hor, &vert) == 3){
PRINT("DILUTION: pos=%.2f, hor=%.2f, vert=%.2f", pos, hor, vert);
}
ret:
PRINT("\n");
}
// 213457.00,4340.59415,N,04127.47560,E,1,05,2.58,1275.8,M,17.0,M,,*60
void gga(_U_ uint8_t *buf){
//DBG("gga: %s\n", buf);
}
//4340.59415,N,04127.47560,E,213457.00,A,A*60
void gll(_U_ uint8_t *buf){
//DBG("gll: %s\n", buf);
}
// ,T,,M,2.494,N,4.618,K,A*23
void vtg(_U_ uint8_t *buf){
//DBG("vtg: %s\n", buf);
}
void txt(_U_ uint8_t *buf){
DBG("txt: %s\n", buf);
}
/**
* PUBX,00
* $PUBX,00,hhmmss.ss,Latitude,N,Longitude,E,AltRef,NavStat,Hacc,Vacc,SOG,COG,Vvel,ageC,HDOP,VDOP,TDOP,GU,RU,DR,*cs
* here buf starts from hhmmss.ss == 1
* 1 = UTC time
* 2 = lattitude
* 3 = N/S
* 4 = longitude
* 5 = E/W
* 6 = altitude
* 7 = nav.stat: NF/DR/G2/G3/D2/D3/RK/TT
* 8 = horizontal accuracy
* 9 = vertical accuracy
* 10 = speed over ground (km/h)
* 11 = cource over ground (deg)
* 12 = vertical velocy ("+" -- down, "-" -- up)
* 13 = age of most recent DGPS correction
* 14 = hor. dilution
* 15 = vert. dilution
* 16 = time dilution
* 17 = number of GPS satell. used
* 18 = number of GLONASS sat. used
* 19 = DR used
* $PUBX,00,113123.00,4340.61823,N,04127.45581,E,1295.919,G2,30,6.9,1.875,38.17,0.000,,2.77,1.00,1.41,3,0,0*4C
*/
void pubx(uint8_t *buf){
//DBG("pubx_00: %s\n", buf);
int H, M, LO, LA, gps, glo, dr;
double S, longitude, lattitude, altitude, hacc, vacc, speed, track, vertspd,
age, hdop, vdop, tdop;
char north = '0', east = '0';
sscanf((char*)buf, "%2d%2d%lf", &H, &M, &S);
PRINT("time: %02d:%02d:%05.2f", H, M, S);
PRINT(" timediff: %g", timediff(H, M, S));
NEXT();
sscanf((char*)buf, "%2d%lf", &LA, &lattitude);
NEXT();
if(*buf != ','){
north = *buf;
lattitude = (double)LA + lattitude / 60.;
if(north == 'S') lattitude = -lattitude;
PRINT(" latt: %g", lattitude);
Latt_mean += lattitude;
Latt_sq += lattitude*lattitude;
++Latt_N;
}
NEXT();
sscanf((char*)buf, "%3d%lf", &LO, &longitude);
NEXT();
if(*buf != ','){
east = *buf;
longitude = (double)LO + longitude / 60.;
if(east == 'W') longitude = -longitude;
PRINT(" long: %g", longitude);
Long_mean += longitude;
Long_sq += longitude*longitude;
++Long_N;
}
NEXT();
#define FSCAN(par, nam) do{if(*buf != ','){sscanf((char*)buf, "%lf", &par); \
PRINT(" " nam ": %g", par);} NEXT();}while(0)
FSCAN(altitude, "altitude");
if(*buf != ','){
PRINT(" nav. status: %c%c", buf[0], buf[1]);
}
NEXT();
FSCAN(hacc,"hor.accuracy");
FSCAN(vacc, "vert.accuracy");
FSCAN(speed,"speed");
FSCAN(track,"cource");
FSCAN(vertspd,"vertical speed ('+'-down)");
FSCAN(age,"DGPS age");
FSCAN(hdop, "hor. dilution");
FSCAN(vdop, "vert. dilution");
FSCAN(tdop, "time dilution");
#undef FSCAN
#define ISCAN(par, nam) do{if(*buf != ','){sscanf((char*)buf, "%d", &par); \
PRINT(" " nam ": %d", par);} NEXT();}while(0)
ISCAN(gps, "GPS used");
ISCAN(glo, "GLONASS used");
ISCAN(dr, "DR used");
#undef ISCAN
ret:
PRINT("\n");
}
/**
* Parce content of buffer with GPS data
* WARNING! This function changes data content
*/
void parce_data(uint8_t *buf){
uint8_t *eol;
//DBG("GOT: %s", buf);
while(*buf && (eol = (uint8_t*)strchr((char*)buf, '\r'))){
*eol = 0;
// now make checksum checking:
if(!checksum(buf)) goto cont;
if(strncmp((char*)buf, "$GP", 3)){
if(strncmp((char*)buf, "$PUBX,00,", 9) == 0){
buf += 9;
pubx(buf);
}else{
DBG("Bad string: %s\n", buf);
}
goto cont;
}
buf += 3;
// PARSE variants: GSV, RMC, GSA, GGA, GLL, VTG, TXT
// 1st letter, cold be one of G,R,V or T
switch(*buf){
case 'G': // GSV, GSA, GGA, GLL
++buf;
if(strncmp((char*)buf, "SV", 2) == 0){
gsv(buf+3);
}else if(strncmp((char*)buf, "SA", 2) == 0){
gsa(buf+3);
}else if(strncmp((char*)buf, "GA", 2) == 0){
gga(buf+3);
}else if(strncmp((char*)buf, "LL", 2) == 0){
gll(buf+3);
}else{
DBG("Unknown: $GPG%s", buf);
goto cont;
}
break;
case 'R': // RMC
++buf;
if(strncmp((char*)buf, "MC", 2) == 0){
rmc(buf+3);
}else{
DBG("Unknown: $GPR%s", buf);
goto cont;
}
break;
case 'V': // VTG
++buf;
if(strncmp((char*)buf, "TG", 2) == 0){
vtg(buf+3);
}else{
DBG("Unknown: $GPV%s", buf);
goto cont;
}
break;
case 'T': // TXT
++buf;
if(strncmp((char*)buf, "XT", 2) == 0){
txt(buf+3);
}else{
DBG("Unknown: $GPT%s", buf);
goto cont;
}
break;
default:
DBG("Unknown: $GP%s", buf);
goto cont;
}
cont:
if(eol[1] != '\n') break;
buf = eol + 2;
}
}
/**
* set rate for given NMEA field
* @param field - name of NMEA field
* @param rate - rate in seconds (0 disables field)
* @return -1 if fails, rate if OK
*/
int nmea_fieldrate(uint8_t *field, int rate){
uint8_t buf[256];
if(rate < 0) return -1;
snprintf((char*)buf, 255, "$PUBX,40,%s,0,%d,0,0*", field, rate);
if(write_with_checksum(buf)) return rate;
else return -1;
}
int main(int argc, char **argv){
check4running(argv, PIDFILE, NULL);
initial_setup();
GP = parce_args(argc, argv);
assert(GP);
DBG("Device path: %s\n", GP->devpath);
tty_init(GP->devpath);
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, signals); // hup - quit
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
int i;
if(GP->date) GP->block_msg[GPRMC] = 1; // we calculate date in RMC events
for(i = 0; i < GPMAXMSG; ++i){
int block = 0;
if(GP->block_msg[i])
block = 1; // unblock message
if(nmea_fieldrate((uint8_t*)GPmsgs[i], block) != block)
WARN("Can't %sblock %s!", block?"un":"", GPmsgs[i]);
else
PRINT("%sblock %s\n", block?"un":"", GPmsgs[i]);
}
if(GP->stationary){
PRINT("stationary config\n");
cfg_stationary();
}
double T, T0 = dtime(), Tpoll = 0., tmout = GP->polltmout;
uint8_t *str = NULL;
while(((T=dtime()) - T0 < tmout) || GP->date){ // if we want get date, we should wait a lot
if(GP->pollubx && T-Tpoll > GP->pollinterval){
Tpoll = T;
write_tty((uint8_t*)"$PUBX,00*33\r\n", 13);
}
if((str = get_portdata())){
parce_data(str);
}
}
if(GP->gettimediff)
printf("\nAverage time difference (local-GPS) = %g seconds\n", timediff_aver/timediff_N);
if(GP->meancoords){
printf("\nAverage coordinates:\n\tLattitude");
double mean, std, s;
int d, m;
if(Latt_N){
mean = Latt_mean / Latt_N;
std = sqrt(Latt_sq/Latt_N - mean*mean);
d = (int)mean; m = (int)(60.*(mean-d)); s = 3600*fabs(mean-d-m/60.);
if(m < 0) m = -m;
printf(" = %.6f (%d %d' %.2f''), std = %g (%.2f'')\n", mean, d, m, s, std, std*3600.);
}else printf(": no data\n");
printf("\tLongitude");
if(Long_N){
mean = Long_mean / Long_N;
std = sqrt(Long_sq/Long_N - mean*mean);
d = (int)mean; m = (int)(60.*(mean-d)); s = 3600*fabs(mean-d-m/60.);
if(m < 0) m = -m;
printf(" = %.6f (%d %d' %.2f''), std = %g (%.2f'')\n", mean, d, m, s, std, std*3600.);
}else printf(": no data\n");
}
signals(0);
return 0;
}

296
_deprecated/GPS/parceargs.c Normal file
View File

@@ -0,0 +1,296 @@
/*
* 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"
// 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
};
if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong
fprintf(stderr, "Wrong helpstring!\n");
exit(-1);
}
helpstring = s;
}
/**
* 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];
if(opt == 0 && opts->has_arg == 0) continue; // only long option changing integer flag
// 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:
if(opts->argptr) *((int*)opts->argptr) = 1; // set argptr to 1
break;
case arg_int:
result = myatoll(opts->argptr, optarg, arg_int);
break;
case arg_longlong:
result = myatoll(opts->argptr, optarg, arg_longlong);
break;
case arg_double:
result = myatod(opts->argptr, optarg, arg_double);
break;
case arg_float:
result = myatod(opts->argptr, optarg, arg_float);
break;
case arg_string:
result = (*((char **)opts->argptr) = strdup(optarg));
break;
case arg_function:
result = ((argfn)opts->argptr)(optarg, optind);
break;
}
if(!result){
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(), by 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
_deprecated/GPS/parceargs.h Normal file
View 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__

View File

@@ -0,0 +1,320 @@
/*
* 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"
/**
* 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;
}
/******************************************************************************\
* 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;
}
static 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");
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
textdomain(GETTEXT_PACKAGE);
#endif
}
/******************************************************************************\
* 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;
}
/**
* 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
\******************************************************************************/
static struct termios oldt, newt; // terminal flags
static int console_changed = 0;
// run on exit:
void restore_console(){
if(console_changed)
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
console_changed = 0;
}
// initial setup:
void setup_con(){
if(console_changed) return;
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?
}
console_changed = 1;
}
/**
* 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() without need of pressing ENTER
int ret;
do ret = read_console();
while(ret == 0);
return ret;
}
/******************************************************************************\
* TTY with select()
\******************************************************************************/
static struct termio oldtty, tty; // TTY flags
static int comfd = -1; // TTY fd
// run on exit:
void restore_tty(){
if(comfd == -1) return;
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
close(comfd);
comfd = -1;
}
#ifndef BAUD_RATE
#define BAUD_RATE B9600
#endif
// init:
void tty_init(char *comdev){
DBG("\nOpen port...\n");
if ((comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){
WARN("Can't use port %s\n",comdev);
ioctl(comfd, TCSANOW, &oldtty); // return TTY to previous state
close(comfd);
exit(1); // quit?
}
DBG(" OK\nGet current settings... ");
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
DBG(" 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;
}
int write_tty(uint8_t *buff, size_t length){
ssize_t L = write(comfd, buff, length);
if((size_t)L != length){
WARN("Write error!");
return 1;
}
return 0;
}

View File

@@ -0,0 +1,123 @@
/*
* 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>
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
/*
* GETTEXT
*/
#include <libintl.h>
#define _(String) gettext(String)
#define gettext_noop(String) String
#define N_(String) gettext_noop(String)
#else
#define _(String) (String)
#define N_(String) (String)
#endif
#include <stdlib.h>
#include <termios.h>
#include <termio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdint.h>
// 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)
double dtime();
// 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);
void restore_console();
void setup_con();
int read_console();
int mygetchar();
void restore_tty();
void tty_init(char *comdev);
size_t read_tty(uint8_t *buff, size_t length);
int write_tty(uint8_t *buff, size_t length);
#endif // __USEFULL_MACROS_H__

View File

@@ -0,0 +1,13 @@
CC=gcc
CFLAGS= -Wall -Werror -Wextra -O3 `pkg-config --libs --cflags ncurses`
all: menu mouse_menu acs rolling readf ncurses_and_readline
ncurses_and_readline: ncurses_and_readline.c
@echo -e "\t\tCC $<"
$(CC) $(CFLAGS) -lreadline -o $@ $<
%: %.c
@echo -e "\t\tCC $<"
$(CC) $(CFLAGS) -o $@ $<

45
_deprecated/Ncurses/acs.c Normal file
View File

@@ -0,0 +1,45 @@
#include <ncurses.h>
int main()
{
initscr();
printw("Upper right corner "); addch(ACS_URCORNER); printw("\n");
printw("Upper left corner "); addch(ACS_ULCORNER); printw("\n");
printw("Lower left corner "); addch(ACS_LLCORNER); printw("\n");
printw("Lower right corner "); addch(ACS_LRCORNER); printw("\n");
printw("Tee pointing right "); addch(ACS_LTEE); printw("\n");
printw("Tee pointing left "); addch(ACS_RTEE); printw("\n");
printw("Tee pointing up "); addch(ACS_BTEE); printw("\n");
printw("Tee pointing down "); addch(ACS_TTEE); printw("\n");
printw("Horizontal line "); addch(ACS_HLINE); printw("\n");
printw("Vertical line "); addch(ACS_VLINE); printw("\n");
printw("Large Plus or cross over "); addch(ACS_PLUS); printw("\n");
printw("Scan Line 1 "); addch(ACS_S1); printw("\n");
printw("Scan Line 3 "); addch(ACS_S3); printw("\n");
printw("Scan Line 7 "); addch(ACS_S7); printw("\n");
printw("Scan Line 9 "); addch(ACS_S9); printw("\n");
printw("Diamond "); addch(ACS_DIAMOND); printw("\n");
printw("Checker board (stipple) "); addch(ACS_CKBOARD); printw("\n");
printw("Degree Symbol "); addch(ACS_DEGREE); printw("\n");
printw("Plus/Minus Symbol "); addch(ACS_PLMINUS); printw("\n");
printw("Bullet "); addch(ACS_BULLET); printw("\n");
printw("Arrow Pointing Left "); addch(ACS_LARROW); printw("\n");
printw("Arrow Pointing Right "); addch(ACS_RARROW); printw("\n");
printw("Arrow Pointing Down "); addch(ACS_DARROW); printw("\n");
printw("Arrow Pointing Up "); addch(ACS_UARROW); printw("\n");
printw("Board of squares "); addch(ACS_BOARD); printw("\n");
printw("Lantern Symbol "); addch(ACS_LANTERN); printw("\n");
printw("Solid Square Block "); addch(ACS_BLOCK); printw("\n");
printw("Less/Equal sign "); addch(ACS_LEQUAL); printw("\n");
printw("Greater/Equal sign "); addch(ACS_GEQUAL); printw("\n");
printw("Pi "); addch(ACS_PI); printw("\n");
printw("Not equal "); addch(ACS_NEQUAL); printw("\n");
printw("UK pound sign "); addch(ACS_STERLING); printw("\n");
refresh();
getch();
endwin();
return 0;
}

View File

@@ -0,0 +1,94 @@
#include <stdio.h>
#include <ncurses.h>
#define WIDTH 30
#define HEIGHT 10
int startx = 0;
int starty = 0;
char *choices[] = {
"Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Exit",
};
int n_choices = sizeof(choices) / sizeof(char *);
void print_menu(WINDOW *menu_win, int highlight);
int main()
{ WINDOW *menu_win;
int highlight = 1;
int choice = 0;
int c;
initscr();
clear();
noecho();
cbreak(); /* Line buffering disabled. pass on everything */
startx = (80 - WIDTH) / 2;
starty = (24 - HEIGHT) / 2;
menu_win = newwin(HEIGHT, WIDTH, starty, startx);
keypad(menu_win, TRUE);
mvprintw(0, 0, "Use arrow keys to go up and down, Press enter to select a choice");
refresh();
print_menu(menu_win, highlight);
while(1)
{ c = wgetch(menu_win);
switch(c)
{ case KEY_UP:
if(highlight == 1)
highlight = n_choices;
else
--highlight;
break;
case KEY_DOWN:
if(highlight == n_choices)
highlight = 1;
else
++highlight;
break;
case 10:
choice = highlight;
break;
default:
mvprintw(24, 0, "Charcter pressed is = %3d", c);
if(c > 32 && c < 256) printw(" (%c)", c);
clrtoeol();
refresh();
break;
}
print_menu(menu_win, highlight);
if(choice != 0) /* User did a choice come out of the infinite loop */
break;
}
mvprintw(23, 0, "You chose choice %d with choice string %s\n", choice, choices[choice - 1]);
clrtoeol();
refresh();
sleep(1);
endwin();
return 0;
}
void print_menu(WINDOW *menu_win, int highlight)
{
int x, y, i;
x = 2;
y = 2;
box(menu_win, 0, 0);
for(i = 0; i < n_choices; ++i)
{ if(highlight == i + 1) /* High light the present choice */
{ wattron(menu_win, A_REVERSE);
mvwprintw(menu_win, y, x, "%s", choices[i]);
wattroff(menu_win, A_REVERSE);
}
else
mvwprintw(menu_win, y, x, "%s", choices[i]);
++y;
}
wrefresh(menu_win);
}

View File

@@ -0,0 +1,132 @@
#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#define WIDTH 30
#define HEIGHT 10
int startx = 0;
int starty = 0;
char *choices[] = { "Choice 1",
"Choice 2",
"Choice 3",
"Choice 4",
"Exit",
};
int n_choices = sizeof(choices) / sizeof(char *);
void print_menu(WINDOW *menu_win, int choice);
void report_choice(int mouse_x, int mouse_y, int *p_choice);
int main()
{ int c, choice = 0, highlight = 1;
WINDOW *menu_win;
MEVENT event;
/* Initialize curses */
initscr();
clear();
noecho();
cbreak(); //Line buffering disabled. pass on everything
/* Try to put the window in the middle of screen */
startx = (80 - WIDTH) / 2;
starty = (24 - HEIGHT) / 2;
attron(A_REVERSE);
mvprintw(23, 1, "startx=%d, starty=%d", startx, starty);
refresh();
attroff(A_REVERSE);
/* Print the menu for the first time */
menu_win = newwin(HEIGHT, WIDTH, starty, startx);
keypad(menu_win, TRUE);
print_menu(menu_win, 1);
/* Get all the mouse events */
mousemask(ALL_MOUSE_EVENTS, NULL);
while(1)
{ c = wgetch(menu_win);
mvprintw(4, 1, "Character pressed is = %3d", c);
refresh();
switch(c)
{ case KEY_MOUSE:
if(getmouse(&event) == OK){ /* When the user clicks left mouse button */
mvprintw(5, 1, "x=%d, y=%d, bstate=0x%08x", event.x, event.y, event.bstate);
clrtoeol();
refresh();
if(event.bstate & (BUTTON1_PRESSED|BUTTON1_CLICKED))
{
report_choice(event.x + 1, event.y + 1, &choice);
if(choice == -1) //Exit chosen
goto end;
mvprintw(22, 1, "Choice made is : %d String Chosen is \"%10s\"", choice, choices[choice - 1]);
clrtoeol();
}
}
break;
case KEY_UP:
refresh();
if(highlight == 1)
highlight = n_choices;
else
--highlight;
break;
case KEY_DOWN:
if(highlight == n_choices)
highlight = 1;
else
++highlight;
break;
case 10:
choice = highlight;
mvprintw(2, 1, "Choice: %d", choice);
break;
}
print_menu(menu_win, highlight);
refresh();
if(choice == n_choices) break;
}
end:
endwin();
return 0;
}
void print_menu(WINDOW *menu_win, int choice)
{
int x, y, i;
x = 2;
y = 2;
box(menu_win, 0, 0);
for(i = 0; i < n_choices; ++i)
{ if(choice == i + 1)
{ wattron(menu_win, A_REVERSE);
mvwprintw(menu_win, y, x, "%s", choices[i]);
wattroff(menu_win, A_REVERSE);
}
else
mvwprintw(menu_win, y, x, "%s", choices[i]);
++y;
}
wrefresh(menu_win);
}
/* Report the choice according to mouse position */
void report_choice(int mouse_x, int mouse_y, int *p_choice)
{ int i,j, choice;
i = startx + 2;
j = starty + 3;
for(choice = 0; choice < n_choices; ++choice)
if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + (int)strlen(choices[choice]))
{ if(choice == n_choices - 1)
*p_choice = -1;
else
*p_choice = choice + 1;
break;
}
}

View File

@@ -0,0 +1 @@
-std=c17

View File

@@ -0,0 +1,2 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42

View File

@@ -0,0 +1 @@
[General]

View File

@@ -0,0 +1 @@
-std=c++17

View File

@@ -0,0 +1,7 @@
acs.c
menu.c
mouse_menu.c
ncurses_and_readline.c
rolling.c
scroll.c
scrollfile.c

View File

@@ -0,0 +1 @@
.

View File

@@ -0,0 +1,336 @@
/*
* This file is part of the ncurses project.
* Copyright 2020 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
// based on https://stackoverflow.com/a/28709979/1965803 ->
// https://github.com/ulfalizer/readline-and-ncurses
// Copyright (c) 2015-2019, Ulf Magnusson
// SPDX-License-Identifier: ISC
#include <curses.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Keeps track of the terminal mode so we can reset the terminal if needed on errors
static bool visual_mode = false;
// insert commands when true; roll upper screen when false
static bool insert_mode = true;
static bool should_exit = false;
static void fail_exit(const char *msg){
// Make sure endwin() is only called in visual mode. As a note, calling it
// twice does not seem to be supported and messed with the cursor position.
if(visual_mode) endwin();
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
static WINDOW *msg_win; // Message window
static WINDOW *sep_win; // Separator line above the command (readline) window
static WINDOW *cmd_win; // Command (readline) window
// string list
typedef struct _Line{
int Nline;
char *contents;
struct _Line *prev, *next;
} Line;
// head of list, current item and first line on screen
Line *head = NULL, *curr = NULL, *firstline = NULL;
int nr_lines = 0; // total anount of data portions @ input
static unsigned char input; // Input character for readline
// Used to signal "no more input" after feeding a character to readline
static bool input_avail = false;
// Not bothering with 'input_avail' and just returning 0 here seems to do the
// right thing too, but this might be safer across readline versions
static int readline_input_avail(){
return input_avail;
}
static int readline_getc(__attribute__((__unused__)) FILE *dummy){
input_avail = false;
return input;
}
static void forward_to_readline(char c){
input = c;
input_avail = true;
rl_callback_read_char();
}
static void msg_win_redisplay(bool for_resize){
werase(msg_win);
Line *l = firstline;
int nlines = 0; // total amount of lines @ output
for(; l && (nlines < LINES - 2); l = l->next){
size_t contlen = strlen(l->contents) + 128;
char *buf = malloc(contlen);
// don't add trailing '\n' (or last line will be empty with cursor)
contlen = snprintf(buf, contlen, "%s", l->contents);
int nlnext = (contlen - 1) / COLS + 1;
wmove(msg_win, nlines, 0);
if(nlines + nlnext < LINES-2){ // can put out the full line
waddstr(msg_win, buf);
//wprintw(msg_win, "%d (%d): %s -> %d", l->Nline, firstline->Nline, l->contents, nlnext);
nlines += nlnext;
}else{ // put only first part
int rest = LINES-2 - nlines;
waddnstr(msg_win, buf, rest *COLS);
break;
}
free(buf);
}
curs_set(0);
if(for_resize) wnoutrefresh(msg_win);
else wrefresh(msg_win);
}
static void got_command(char *line){
if(!line) // Ctrl-D pressed on empty line
should_exit = true;
else{
if(!*line) return; // zero length
add_history(line);
Line *lp = malloc(sizeof(Line));
lp->contents = line;
lp->prev = curr;
lp->next = NULL;
lp->Nline = nr_lines++;
if(!curr || !head){
head = curr = firstline = lp;
}else
curr->next = lp;
curr = lp;
// roll back to show last input
if(curr->prev){
firstline = curr;
int totalln = (strlen(firstline->contents) - 1)/COLS + 1;
while(firstline->prev){
totalln += (strlen(firstline->prev->contents) - 1)/COLS + 1;
if(totalln > LINES-2) break;
firstline = firstline->prev;
}
}
msg_win_redisplay(false);
}
}
static void cmd_win_redisplay(bool for_resize){
int cursor_col = 2 + rl_point; // "> " width is 2
werase(cmd_win);
int x = 0, maxw = COLS-2;
if(cursor_col > maxw){
x = cursor_col - maxw;
cursor_col = maxw;
}
char abuf[4096];
snprintf(abuf, 4096, "> %s", rl_line_buffer);
waddstr(cmd_win, abuf+x);
wmove(cmd_win, 0, cursor_col);
curs_set(2);
if(for_resize) wnoutrefresh(cmd_win);
else wrefresh(cmd_win);
}
static void readline_redisplay(void){
cmd_win_redisplay(false);
}
static void show_mode(bool for_resize){
wclear(sep_win);
if(insert_mode) waddstr(sep_win, "INSERT (TAB to switch, ctrl+D to quit)");
else waddstr(sep_win, "SCROLL (TAB to switch, q to quit)");
if(for_resize) wnoutrefresh(sep_win);
else wrefresh(sep_win);
cmd_win_redisplay(for_resize);
}
static void resize(void){
if(LINES > 2){
wresize(msg_win, LINES - 2, COLS);
wresize(sep_win, 1, COLS);
wresize(cmd_win, 1, COLS);
mvwin(sep_win, LINES - 2, 0);
mvwin(cmd_win, LINES - 1, 0);
}
msg_win_redisplay(true);
show_mode(true);
doupdate();
}
static void init_ncurses(void){
if (!initscr())
fail_exit("Failed to initialize ncurses");
visual_mode = true;
if(has_colors()){
start_color();
use_default_colors();
}
cbreak();
noecho();
nonl();
intrflush(NULL, FALSE);
// Do not enable keypad() since we want to pass unadulterated input to readline
keypad(cmd_win, 0);
// Explicitly specify a "very visible" cursor to make sure it's at least
// consistent when we turn the cursor on and off (maybe it would make sense
// to query it and use the value we get back too). "normal" vs. "very
// visible" makes no difference in gnome-terminal or xterm. Let this fail
// for terminals that do not support cursor visibility adjustments.
curs_set(2);
if(LINES > 2){
msg_win = newwin(LINES - 2, COLS, 0, 0);
sep_win = newwin(1, COLS, LINES - 2, 0);
cmd_win = newwin(1, COLS, LINES - 1, 0);
}
else{
// Degenerate case. Give the windows the minimum workable size to
// prevent errors from e.g. wmove().
msg_win = newwin(1, COLS, 0, 0);
sep_win = newwin(1, COLS, 0, 0);
cmd_win = newwin(1, COLS, 0, 0);
}
if(!msg_win || !sep_win || !cmd_win)
fail_exit("Failed to allocate windows");
// Allow strings longer than the command window and show only the last part if the string doesn't fit
// scrollok(cmd_win, TRUE);
if(has_colors()){
// Use white-on-blue cells for the separator window...
init_pair(1, COLOR_WHITE, COLOR_BLUE);
wbkgd(sep_win, COLOR_PAIR(1));
}
else
// ...or the "best highlighting mode of the terminal" if it doesn't
// support colors
wbkgd(sep_win, A_STANDOUT);
show_mode(false);
mousemask(BUTTON4_PRESSED|BUTTON5_PRESSED, NULL);
}
static void deinit_ncurses(void){
delwin(msg_win);
delwin(sep_win);
delwin(cmd_win);
endwin();
visual_mode = false;
}
static void init_readline(void){
// Let ncurses do all terminal and signal handling
rl_catch_signals = 0;
rl_catch_sigwinch = 0;
rl_deprep_term_function = NULL;
rl_prep_term_function = NULL;
// Prevent readline from setting the LINES and COLUMNS environment
// variables, which override dynamic size adjustments in ncurses. When
// using the alternate readline interface (as we do here), LINES and
// COLUMNS are not updated if the terminal is resized between two calls to
// rl_callback_read_char() (which is almost always the case).
rl_change_environment = 0;
// Handle input by manually feeding characters to readline
rl_getc_function = readline_getc;
rl_input_available_hook = readline_input_avail;
rl_redisplay_function = readline_redisplay;
rl_callback_handler_install("> ", got_command);
}
static void deinit_readline(void){
rl_callback_handler_remove();
}
static void rolldown(){
if(firstline && firstline->prev){
firstline = firstline->prev;
msg_win_redisplay(false);
}
}
static void rollup(){
if(firstline && firstline->next){
firstline = firstline->next;
msg_win_redisplay(false);
}
}
int main(void){
init_ncurses();
init_readline();
MEVENT event;
do{
int c = wgetch(cmd_win);
bool processed = true;
switch(c){
case KEY_MOUSE:
if(getmouse(&event) == OK){
if(event.bstate & (BUTTON4_PRESSED)) rolldown(); // wheel up
else if(event.bstate & (BUTTON5_PRESSED)) rollup(); // wheel down
}
break;
case '\t': // tab switch between scroll and edit mode
keypad(cmd_win, insert_mode); // enable/disable reaction @ special characters
insert_mode = !insert_mode;
show_mode(false);
break;
case KEY_RESIZE:
resize();
break;
default:
processed = false;
}
if(processed) continue;
if(insert_mode){
forward_to_readline(c);
}else{
switch(c){
case KEY_UP: // roll down for one item
rolldown();
break;
case KEY_DOWN: // roll up for one item
rollup();
break;
case KEY_PPAGE: // PageUp: roll down for 10 items
for(int i = 0; i < 10; ++i){
if(firstline && firstline->prev) firstline = firstline->prev;
else break;
}
msg_win_redisplay(false);
break;
case KEY_NPAGE: // PageUp: roll up for 10 items
for(int i = 0; i < 10; ++i){
if(firstline && firstline->next) firstline = firstline->next;
else break;
}
msg_win_redisplay(false);
break;
default:
if(c == 'q' || c == 'Q') should_exit = true; // quit
}
}
}while(!should_exit);
deinit_ncurses();
deinit_readline();
puts("Shut down cleanly");
return 0;
}

View File

@@ -0,0 +1,28 @@
#include <curses.h>
#include <stdlib.h>
static int maxx, maxy;
void initscreen(){
initscr();
getmaxyx(stdscr, maxy, maxx); // getyx - get current coords
cbreak();
keypad(stdscr, TRUE); // We get F1, F2 etc..
noecho();
nodelay(stdscr, TRUE); // getch() returns ERR if no keys pressed
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
init_pair(6, COLOR_CYAN, COLOR_BLACK);
init_pair(7, COLOR_WHITE, COLOR_BLACK);
clear();
}
int main(){
initscreen();
return 0;
}

View File

@@ -0,0 +1,110 @@
#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXLEN 127
typedef struct Line {
char *contents;
struct Line *prev, *next;
} Line;
char inputstr[MAXLEN+1] = {0};
int currinp = 0;
Line *head = NULL, *curr = NULL, *firstline = NULL;
int refreshmain = 1, refreshstr = 1;
int nr_lines;
int curr_line;
WINDOW *mainwin, *onestring;
int term_rows, term_cols;
void draw(Line *l);
int main(int argc, char **argv)
{
Line *lp;
// printf("\033c"); - cls
initscr();
noecho();
getmaxyx(stdscr, term_rows, term_cols);
term_rows -= 2;
mainwin = newwin(term_rows, term_cols, 0, 0);
onestring = newwin(1, term_cols, term_rows+1, 0);
keypad(onestring, TRUE); // for KEY_UP, KEY_DOWN
curr_line = 0;
draw(curr);
int ch;
while ((ch = wgetch(onestring)) != EOF)
{
if (ch == KEY_UP && firstline->prev != NULL)
{
curr_line--;
firstline = firstline->prev;
refreshmain = 1;
}
else if (ch == KEY_DOWN && firstline->next != NULL && curr_line + term_rows < nr_lines)
{
curr_line++;
firstline = firstline->next;
refreshmain = 1;
}
else if(ch == 10 && currinp){
lp = malloc(sizeof(Line));
lp->contents = strdup(inputstr);
lp->prev = curr;
lp->next = NULL;
if(!curr || !head){
head = curr = firstline = lp;
}else
curr->next = lp;
curr = lp;
++nr_lines;
while(nr_lines - curr_line > term_rows){
curr_line++;
firstline = firstline->next;
}
currinp = 0;
inputstr[0] = 0;
refreshstr = 1;
refreshmain = 1;
}else if(ch >= ' ' && ch < 256 && ch != 127){
if(currinp > MAXLEN){
wclear(onestring);
waddstr(onestring, "Limit reached!");
wrefresh(onestring);
}else{
inputstr[currinp++] = ch;
inputstr[currinp] = 0;
}
refreshstr = 1;
}
draw(firstline);
}
endwin();
return 0;
}
void draw(Line *l){
if(refreshmain){
refreshmain = 0;
wclear(mainwin);
for(int i = 0; i < term_rows && l != NULL; i++, l = l->next)
wprintw(mainwin, "%s\n", l->contents);
wrefresh(mainwin);
}
if(refreshstr){
refreshstr = 0;
wclear(onestring);
wprintw(onestring, " > %s", inputstr);
wrefresh(onestring);
}
}

View File

@@ -0,0 +1,128 @@
#include <ncurses.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXLEN 128
typedef struct Line {
char contents[MAXLEN];
struct Line *prev, *next;
} Line;
#define MAXSTRL 120
char inputstr[MAXSTRL+1] = {0};
int currinp = 0;
Line *head, *curr;
int refreshmain = 1, refreshstr = 1;
int nr_lines;
int curr_line;
WINDOW *mainwin, *onestring;
int term_rows, term_cols;
void load(const char *filename);
void draw(Line *l);
int main(int argc, char **argv)
{
if (argc == 1)
{
fputs("less: No file to open\n", stderr);
return 1;
}
initscr();
noecho();
getmaxyx(stdscr, term_rows, term_cols);
term_rows -= 2;
mainwin = newwin(term_rows, term_cols, 0, 0);
onestring = newwin(1, term_cols, term_rows+1, 0);
keypad(onestring, TRUE); // for KEY_UP, KEY_DOWN
waddstr(mainwin, "Reading text...\n");
wrefresh(mainwin);
sleep(1);
load(argv[1]);
curr = head;
curr_line = 0;
draw(curr);
int ch;
while ((ch = wgetch(onestring)) != EOF)
{
if (ch == KEY_UP && curr->prev != NULL)
{
curr_line--;
curr = curr->prev;
refreshmain = 1;
}
else if (ch == KEY_DOWN && curr->next != NULL
&& curr_line + term_rows < nr_lines)
{
curr_line++;
curr = curr->next;
refreshmain = 1;
}
else if(ch == 10){
currinp = 0;
inputstr[0] = 0;
refreshstr = 1;
}else if(ch >= ' ' && ch < 256 && ch != 127){
if(currinp > MAXSTRL){
wclear(onestring);
waddstr(onestring, "Limit reached!");
wrefresh(onestring);
}else{
inputstr[currinp++] = ch;
inputstr[currinp] = 0;
}
refreshstr = 1;
}
draw(curr);
}
endwin();
return 0;
}
void load(const char *filename)
{
FILE *fp = fopen(filename, "r");
Line *lp;
head = malloc(sizeof(Line));
head->prev = head->next = NULL;
curr = head;
while (fgets(curr->contents, MAXLEN, fp))
{
lp = malloc(sizeof(Line));
lp->prev = curr;
curr->next = lp;
curr = lp;
nr_lines++;
}
curr->next = NULL;
}
void draw(Line *l)
{
if(refreshmain){
refreshmain = 0;
wclear(mainwin);
for(int i = 0; i < term_rows && l != NULL; i++, l = l->next)
waddstr(mainwin, l->contents);
wrefresh(mainwin);
}
if(refreshstr){
refreshstr = 0;
wclear(onestring);
wprintw(onestring, " > %s", inputstr);
wrefresh(onestring);
}
}

View 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)

View 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.

View File

@@ -0,0 +1,499 @@
/*
* 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(char *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");
quit(-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"
"-xxx\tMake xxx steps toward zero's end-switch (0 main infinity)\n"
"+xxx\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"
"R\tTurn relay ON\n"
"r\tTurn relay OFF\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");
quit(-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(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 parse_ctrlr_ans(char *ans){
char *E = NULL, *Star = NULL;
if(!ans || !*ans) return 1;
bus_error = NO_ERROR;
if(!(E = strchr(ans, 'E')) || !(Star = strchr(ans, '*')) || E[1] != '1'){
fprintf(stderr, "Answer format error (got: %s)\n", ans);
bus_error = UNDEFINED_ERR;
return 0;
}
switch (E[2]){ // E = "E1x"
case '0': // 10 - normal execution
break;
case '4': // 14 - program end
printf("Last command exectuted normally\n");
break;
case '2': // command interrupt by other signal
fprintf(stderr, "Last command terminated\n");
break;
case '3':
bus_error = CODE_ERR;
fprintf(stderr, "runtime");
break;
case '5':
bus_error = BUS_ERR;
fprintf(stderr, "data bus");
break;
case '6':
bus_error = COMMAND_ERR;
fprintf(stderr, "command");
break;
case '9':
bus_error = CMD_DATA_ERR;
fprintf(stderr, "command data");
break;
default:
bus_error = UNDEFINED_ERR;
fprintf(stderr, "undefined (%s)", ans);
}
if(bus_error != NO_ERROR){
fprintf(stderr, " error in controller\n");
return 0;
}
return 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;
}
return parse_ctrlr_ans(ans);
}
/*
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("SP*");
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 == '-' || rb == '+'){
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("-+01Rr", 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
if(strchr("-+01", rb)){
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 '+':
Die_on_error("DR*");
if(stepsN)
sprintf(command, "MV%d*", stepsN);
else
sprintf(command, "MV*");
Die_on_error(command);
break;
case '-':
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("SP*");
break;
case 'A':
Die_on_error("ST1*");
break;
case 'E':
erase_ctrlr();
break;
case 'R':
Die_on_error("SF*");
break;
case 'r':
Die_on_error("CF*");
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_:
erase_ctrlr();
}
/**
* 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;
char buff[128], *bufptr = buff;
size_t L;
if(argc == 2){
fout = fopen(argv[1], "a");
if(!fout){
perror("Can't open output file");
return (-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);
erase_ctrlr();
//t0 = dtime();
while(1){
rb = read_console(NULL, 1);
if(rb > 0) con_sig(rb);
L = read_tty(bufptr, 127);
if(L){
bufptr += L;
if(bufptr - buff > 127){
fprintf(stderr, "Error: input buffer overflow!\n");
bufptr = buff;
}
if(bufptr[-1] == '*'){ // end of input command
*bufptr = 0;
parse_ctrlr_ans(buff);
//printf("%s", buff);
if(fout) fprintf(fout, "%zd\t%s\n", time(NULL), buff);
bufptr = buff;
}
}
}
}

View File

@@ -0,0 +1,45 @@
# run `make DEF=...` to add extra defines
PROGRAM := socket
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros -lm -L/usr/local/lib/
SRCS := $(wildcard *.c)
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
OBJDIR := mk
CFLAGS += -O3 -Wno-trampolines -std=gnu99
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
DEPS := $(OBJS:.o=.d)
CC = gcc
#CXX = g++
all : $(PROGRAM)
debug: CFLAGS += -DEBUG -Werror -Wall -Wextra
debug: all
$(OBJS): $(OBJDIR)
$(PROGRAM) : $(OBJS)
@echo -e "\t\tLD $(PROGRAM)"
$(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
$(OBJDIR):
mkdir $(OBJDIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR)/%.o: %.c
@echo -e "\t\tCC $<"
$(CC) $< -MD -c $(CFLAGS) $(DEFINES) -o $@
clean:
@echo -e "\t\tCLEAN"
@rm -f $(OBJS) $(DEPS)
@rmdir $(OBJDIR) 2>/dev/null || true
xclean: clean
@rm -f $(PROGRAM)
.PHONY: clean xclean

View File

@@ -0,0 +1,7 @@
Socket server and client snippet
================================
This snippet allows to create some utilities than can be run both in client or server mode.
The sockets are **local** TCP or UNIX sockets. Server-side polling use `poll()`.
The pieces of user code may be in comments marked `USERCODE`

View File

@@ -0,0 +1,120 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
// client-side functions
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <usefull_macros.h>
#include "client.h"
#include "socket.h"
/**
* @brief processData - process here some actions and make messages for server
* @param buf (o) - buffer for answer
* @param l - max length of buf
* @return amount of answer bytes
*/
static int process_data(char *buf, int l){
if(!buf || !l) return 0;
/* USERCODE: get here some data to send */
static double t0 = 0.;
if(dtime() - t0 > 1.){
t0 = dtime();
// send random commands each 1 second
int x = rand() % 600;
const char *cmd = NULL;
if(x < 100) cmd = "time";
else if(x < 200) cmd = "getval1";
else if(x < 300) cmd = "getval2";
else if(x < 400) cmd = "ping";
if(cmd) snprintf(buf, l, "%s", cmd);
else{
if(x < 500) snprintf(buf, l, "setval1=%d", rand() & 0xff);
else snprintf(buf, l, "setval2=%d", rand() & 0xff);
}
return strlen(buf);
}
return 0;
}
/**
* @brief process_server_message - parse messages from server and make an answer
* @param buf (io) - incoming message (the answer will be in this buffer)
* @param l - buff max length
* @return amount of answer bytes
*/
static int process_server_message(char *buf, int l){
/* USERCODE inside this funtion */
if(!buf || !l) return 0;
// just show on screen
green("SERVER send: %s\n", buf);
return 0;
}
/**
* check data from fd (polling function for client)
* @param fd - file descriptor
* @return 0 in case of timeout, 1 in case of fd have data, -1 if error
*/
static int canberead(int fd){
fd_set fds;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100;
FD_ZERO(&fds);
FD_SET(fd, &fds);
do{
int rc = select(fd+1, &fds, NULL, NULL, &timeout);
if(rc < 0){
if(errno != EINTR){
LOGWARN("select()");
WARN("select()");
return -1;
}
continue;
}
break;
}while(1);
if(FD_ISSET(fd, &fds)){
//DBG("FD_ISSET");
return 1;
}
return 0;
}
void client(int sock){
char buf[BUFLEN];
while(1){
int l = process_data(buf, BUFLEN-1);
if(l) sendmessage(sock, buf, l);
if(1 != canberead(sock)) continue;
int n = read(sock, buf, BUFLEN-1);
if(n == 0){
WARNX("Server disconnected");
signals(0);
}
buf[n] = 0;
DBG("Got from server: %s", buf);
LOGMSG("Got from server: %s", buf);
l = process_server_message(buf, n);
if(l) sendmessage(sock, buf, l);
}
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef CLIENT_H__
#define CLIENT_H__
// client-side functions
void client(int fd);
#endif // CLIENT_H__

View File

@@ -0,0 +1,83 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include "cmdlnopts.h"
#include "usefull_macros.h"
// default PID filename:
#define DEFAULT_PIDFILE "/tmp/usbsock.pid"
static int help;
static glob_pars G = {
.pidfile = DEFAULT_PIDFILE,
};
glob_pars *GP = &G;
/*
* Define command line options by filling structure:
* name has_arg flag val type argptr help
*/
static myoption cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")},
{"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
{"client", NO_ARGS, NULL, 'c', arg_int, APTR(&G.client), _("run as client")},
{"path", NEED_ARG, NULL, 'N', arg_string, APTR(&G.path), _("UNIX socket path/name (start from \\0 for no files)")},
{"port", NEED_ARG, NULL, 'P', arg_string, APTR(&G.port), _("port to connect (for local TCP socket)")},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), _("increase log verbose level (default: LOG_WARN) and messages (default: none)")},
end_option
};
/**
* Parse 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
*/
void parse_args(int argc, char **argv){
int i;
size_t hlen = 1024;
char helpstring[1024], *hptr = helpstring;
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
// format of help: "Usage: progname [args]\n"
change_helpstring(helpstring);
// parse arguments
parseargs(&argc, &argv, cmdlnopts);
for(i = 0; i < argc; i++)
printf("Ignore parameter\t%s\n", argv[i]);
if(help) showhelp(-1, cmdlnopts);
}
/**
* @brief verbose - print additional messages depending of G.verbose (add '\n' at end)
* @param levl - message level
* @param fmt - message
*/
void verbose(int levl, const char *fmt, ...){
va_list ar;
if(levl > G.verbose) return;
va_start(ar, fmt);
vprintf(fmt, ar);
va_end(ar);
printf("\n");
}

View File

@@ -0,0 +1,40 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef CMDLNOPTS_H__
#define CMDLNOPTS_H__
/*
* here are some typedef's for global data
*/
typedef struct{
char *pidfile; // name of PID file
char *logfile; // logging to this file
char *path; // path to UNIX-socket file
char *port; // local TCP socket port
int verbose; // verbose level: for messages & logging
int client; // ==1 if application runs in client mode
} glob_pars;
extern glob_pars *GP;
void parse_args(int argc, char **argv);
void verbose(int levl, const char *fmt, ...);
#endif // CMDLNOPTS_H__

View File

@@ -0,0 +1,108 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <usefull_macros.h>
#include "cmdlnopts.h"
#include "socket.h"
static pid_t childpid = 0;
static int isserver = 1;
void signals(int sig){
if(childpid){ // slave process
DBG("Child killed with sig=%d", sig);
LOGWARN("Child killed with sig=%d", sig);
exit(sig);
}
// master process
DBG("Master process");
if(sig){
DBG("Exit with signal %d", sig);
signal(sig, SIG_IGN);
LOGERR("Exit with signal %d", sig);
}else LOGERR("Exit");
if(GP->pidfile && isserver){
DBG("Unlink pid file");
unlink(GP->pidfile);
}
exit(sig);
}
int main(int argc, char **argv){
char *self = strdup(argv[0]);
initial_setup();
parse_args(argc, argv);
if(GP->logfile){
int lvl = LOGLEVEL_WARN + GP->verbose;
DBG("level = %d", lvl);
if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY;
verbose(1, "Log file %s @ level %d\n", GP->logfile, lvl);
OPENLOG(GP->logfile, lvl, 1);
if(!globlog) WARNX("Can't create log file");
}
if(GP->client) isserver = 0;
if(GP->port){
if(GP->path){
WARNX("Options `port` and `path` can't be used together! Point `port` for TCP socket or `path` for UNIX.");
return 1;
}
int port = atoi(GP->port);
if(port < PORTN_MIN || port > PORTN_MAX){
WARNX("Wrong port value: %d", port);
return 1;
}
}else if(!GP->path) ERRX("You should point option `port` or `path`!");
if(isserver) check4running(self, GP->pidfile);
// signal reactions:
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
LOGMSG("Started");
#ifndef EBUG
if(isserver){
unsigned int pause = 5;
while(1){
childpid = fork();
if(childpid){ // master
double t0 = dtime();
LOGMSG("Created child with pid %d", childpid);
wait(NULL);
LOGWARN("Child %d died", childpid);
if(dtime() - t0 < 1.) pause += 5;
else pause = 1;
if(pause > 900) pause = 900;
sleep(pause); // wait a little before respawn
}else{ // slave
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
break;
}
}
}
#endif
if(GP->path) return start_socket(isserver, GP->path, FALSE);
if(GP->port) return start_socket(isserver, GP->port, TRUE);
}

View File

@@ -0,0 +1,150 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <netdb.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <usefull_macros.h>
#include "server.h"
#include "socket.h"
// server-side functions
// command handlers
hresult pinghandler(int fd, _U_ const char *key, _U_ const char *val){
sendstrmessage(fd, "PING");
return RESULT_SILENCE;
}
static int sendtime = 0;
hresult timehandler(_U_ int fd, _U_ const char *key, _U_ const char *val){
sendtime = 1;
return RESULT_OK;
}
static int value1 = 0, value2 = 0;
hresult valsethandler(_U_ int fd, const char *key, const char *val){
if(!val || !*val) return RESULT_BADVAL;
int i = atoi(val);
if(i < -65535 || i > 65535) return RESULT_BADVAL;
if(strcmp(key, "setval1") == 0) value1 = i;
else value2 = i;
return RESULT_OK;
}
hresult valgethandler(int fd, const char *key, _U_ const char *val){
char buf[32];
if(strcmp(key, "getval1") == 0) snprintf(buf, 32, "VAL1=%d", value1);
else snprintf(buf, 32, "VAL2=%d", value2);
sendstrmessage(fd, buf);
return RESULT_SILENCE;
}
/*
hresult handler(_U_ int fd, _U_ const char *key, _U_ const char *val){
;
}
*/
static handleritem items[] = {
{pinghandler, "ping"},
{timehandler, "time"},
{valsethandler, "setval1"},
{valsethandler, "setval2"},
{valgethandler, "getval1"},
{valgethandler, "getval2"},
{NULL, NULL},
};
/**
* @brief processData - process here some actions and make messages for all clients
* @param buf (o) - buffer for answer
* @param l - max length of buf
* @return amount of answer bytes
*/
static int process_data(char *buf, int l){
if(!buf || !l) return 0;
/* USERCODE: get here some data to send all clients */
if(sendtime){
snprintf(buf, l, "TIME=%.2f", dtime());
sendtime = 0;
return strlen(buf);
}
return 0;
}
#define CLBUFSZ BUFSIZ
void server(int sock){
if(listen(sock, MAXCLIENTS) == -1){
WARN("listen");
LOGWARN("listen");
return;
}
int nfd = 1; // only one socket @start
struct pollfd poll_set[MAXCLIENTS+1];
char buffers[MAXCLIENTS][CLBUFSZ]; // buffers for data reading
bzero(poll_set, sizeof(poll_set));
// ZERO - listening server socket
poll_set[0].fd = sock;
poll_set[0].events = POLLIN;
while(1){
poll(poll_set, nfd, 1); // max timeout - 1ms
if(poll_set[0].revents & POLLIN){ // check main for accept()
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int client = accept(sock, (struct sockaddr*)&addr, &len);
DBG("New connection");
LOGMSG("SERVER got connection, fd=%d", client);
if(nfd == MAXCLIENTS + 1){
LOGWARN("Max amount of connections, disconnect fd=%d", client);
WARNX("Limit of connections reached");
close(client);
}else{
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
poll_set[nfd].fd = client;
poll_set[nfd].events = POLLIN;
++nfd;
}
}
char buff[BUFLEN];
int l = 0;
// process some data & send messages to ALL
if((l = process_data(buff, BUFLEN-1))){
DBG("Send to %d clients", nfd - 1);
for(int i = 1; i < nfd; ++i)
sendmessage(poll_set[i].fd, buff, l);
}
// scan connections
for(int fdidx = 1; fdidx < nfd; ++fdidx){
if((poll_set[fdidx].revents & POLLIN) == 0) continue;
int fd = poll_set[fdidx].fd;
if(!processData(fd, items, buffers[fdidx-1], CLBUFSZ)){ // socket closed
DBG("Client fd=%d disconnected", fd);
LOGMSG("SERVER client fd=%d disconnected", fd);
buffers[fdidx-1][0] = 0; // clear rest of data in buffer
close(fd);
// move last FD to current position
poll_set[fdidx] = poll_set[nfd - 1];
--nfd;
}
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef SERVER_H__
#define SERVER_H__
// server-side functions
void server(int fd);
#endif // SERVER_H__

View File

@@ -0,0 +1,213 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h> // isspace
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/un.h> // unix socket
#include <usefull_macros.h>
#include "cmdlnopts.h"
#include "client.h"
#include "server.h"
#include "socket.h"
/**
* @brief start_socket - create socket and run client or server
* @param isserver - TRUE for server, FALSE for client
* @param path - UNIX-socket path or local INET socket port
* @param isnet - TRUE for INET socket, FALSE for UNIX
* @return 0 if OK
*/
int start_socket(int isserver, char *path, int isnet){
if(!path) return 1;
DBG("path/port: %s", path);
int sock = -1;
struct addrinfo hints = {0}, *res;
struct sockaddr_un unaddr = {0};
if(isnet){
DBG("Network socket");
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if(getaddrinfo("127.0.0.1", path, &hints, &res) != 0){
ERR("getaddrinfo");
}
}else{
DBG("UNIX socket");
char apath[128];
if(*path == 0){
DBG("convert name");
apath[0] = 0;
strncpy(apath+1, path+1, 126);
}else if(strncmp("\\0", path, 2) == 0){
DBG("convert name");
apath[0] = 0;
strncpy(apath+1, path+2, 126);
}else strcpy(apath, path);
unaddr.sun_family = AF_UNIX;
hints.ai_addr = (struct sockaddr*) &unaddr;
hints.ai_addrlen = sizeof(unaddr);
memcpy(unaddr.sun_path, apath, 106); // if sun_path[0] == 0 we don't create a file
hints.ai_family = AF_UNIX;
hints.ai_socktype = SOCK_SEQPACKET;
res = &hints;
}
for(struct addrinfo *p = res; p; p = p->ai_next){
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0){ // or SOCK_STREAM?
LOGWARN("socket()");
WARN("socket()");
continue;
}
if(isserver){
int reuseaddr = 1;
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
LOGWARN("setsockopt()");
WARN("setsockopt()");
close(sock); sock = -1;
continue;
}
if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){
LOGWARN("bind()");
WARN("bind()");
close(sock); sock = -1;
continue;
}
int enable = 1;
if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking
LOGERR("Can't make socket nonblocking");
ERRX("ioctl()");
}
}else{
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
LOGWARN("connect()");
WARN("connect()");
close(sock); sock = -1;
}
}
break;
}
if(sock < 0){
LOGERR("Can't open socket");
ERRX("Can't open socket");
}
if(isnet) freeaddrinfo(res);
if(isserver) server(sock);
else client(sock);
close(sock);
signals(0);
return 0;
}
// simple wrapper over write: add missed newline and log data
void sendmessage(int fd, const char *msg, int l){
if(fd < 1 || !msg || l < 1) return;
DBG("send to fd %d: %s [%d]", fd, msg, l);
char *tmpbuf = MALLOC(char, l+1);
memcpy(tmpbuf, msg, l);
if(msg[l-1] != '\n') tmpbuf[l++] = '\n';
if(l != send(fd, tmpbuf, l, MSG_NOSIGNAL)){
LOGWARN("write()");
WARN("write()");
}else{
if(globlog){ // logging turned ON
tmpbuf[l-1] = 0; // remove trailing '\n' for logging
LOGMSG("SEND to fd %d: %s", fd, tmpbuf);
}
}
FREE(tmpbuf);
}
void sendstrmessage(int fd, const char *msg){
if(fd < 1 || !msg) return;
int l = strlen(msg);
sendmessage(fd, msg, l);
}
// text messages for `hresult`
static const char *resmessages[] = {
[RESULT_OK] = "OK",
[RESULT_FAIL] = "FAIL",
[RESULT_BADKEY] = "BADKEY",
[RESULT_BADVAL] = "BADVAL",
[RESULT_SILENCE] = "",
};
const char *hresult2str(hresult r){
if(r >= RESULT_NUM) return "BADRESULT";
return resmessages[r];
}
// parse string of data (command or key=val)
// the CONTENT of buffer `str` WILL BE BROKEN!
static void parsestring(int fd, handleritem *handlers, char *str){
if(fd < 1 || !handlers || !handlers->key || !str || !*str) return;
DBG("Got string %s", str);
// remove starting spaces in key
while(isspace(*str)) ++str;
char *val = strchr(str, '=');
if(val){ // get value: remove starting spaces in val
*val++ = 0;
while(isspace(*val)) ++val;
}
// remove trailing spaces in key
char *e = str + strlen(str) - 1; // last key symbol
while(isspace(*e) && e > str) --e;
e[1] = 0;
// now we have key (`str`) and val (or NULL)
DBG("key=%s, val=%s", str, val);
for(handleritem *h = handlers; h->handler; ++h){
if(strcmp(str, h->key) == 0){ // found command
if(h->handler){
hresult r = h->handler(fd, str, val);
sendstrmessage(fd, hresult2str(r));
}else sendstrmessage(fd, resmessages[RESULT_FAIL]);
return;
}
}
sendstrmessage(fd, resmessages[RESULT_BADKEY]);
}
/**
* @brief processData - read (if available) data from fd and run processing, sending to fd messages for each command
* @param fd - socket file descriptor
* @param handlers - NULL-terminated array of handlers
* @param buf (io) - zero-terminated buffer for storing rest of data (without newline), its content will be changed
* @param buflen - its length
* @return FALSE if client closed (nothing to read)
*/
int processData(int fd, handleritem *handlers, char *buf, int buflen){
int curlen = strlen(buf);
if(curlen == buflen-1) curlen = 0; // buffer overflow - clear old content
ssize_t rd = read(fd, buf + curlen, buflen-1 - curlen);
if(rd <= 0) return FALSE;
DBG("got %s[%zd] from %d", buf, rd, fd);
char *restofdata = buf, *eptr = buf + curlen + rd;
*eptr = 0;
do{
char *nl = strchr(restofdata, '\n');
if(!nl) break;
*nl++ = 0;
parsestring(fd, handlers, restofdata);
restofdata = nl;
DBG("rest of data: %s", restofdata);
}while(1);
if(restofdata != buf) memmove(buf, restofdata, eptr - restofdata + 1);
return TRUE;
}

View File

@@ -0,0 +1,56 @@
/*
* This file is part of the socksnippet project.
* Copyright 2022 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef SERSOCK_H__
#define SERSOCK_H__
// max & min TCP socket port number
#define PORTN_MAX (65535)
#define PORTN_MIN (1024)
#define BUFLEN (1024)
// Max amount of connections
#define MAXCLIENTS (30)
typedef enum{
RESULT_OK, // all OK
RESULT_FAIL, // failed running command
RESULT_BADVAL, // bad key's value
RESULT_BADKEY, // bad key
RESULT_SILENCE, // send nothing to client
RESULT_NUM
} hresult;
const char *hresult2str(hresult r);
// fd - socket fd to send private messages, key, val - key and its value
typedef hresult (*mesghandler)(int fd, const char *key, const char *val);
typedef struct{
mesghandler handler;
const char *key;
} handleritem;
int start_socket(int server, char *path, int isnet);
void sendmessage(int fd, const char *msg, int l);
void sendstrmessage(int fd, const char *msg);
int processData(int fd, handleritem *handlers, char *buf, int buflen);
#endif // SERSOCK_H__

View File

@@ -0,0 +1 @@
-std=c17

View File

@@ -0,0 +1,6 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42
#define EBUG
#define _GNU_SOURCE
#define _XOPEN_SOURCE=1111

View File

@@ -0,0 +1 @@
[General]

View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 6.0.0, 2022-02-02T17:26:10. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">2</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">4</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
<valuemap type="QVariantMap" key="CppEditor.QuickFix">
<value type="bool" key="UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/ELECTRONICS/CAN_controller/Socket_CANserver</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -0,0 +1 @@
-std=c++17

View File

@@ -0,0 +1,9 @@
client.c
client.h
cmdlnopts.c
cmdlnopts.h
main.c
server.c
server.h
socket.c
socket.h

View File

@@ -0,0 +1 @@
.

View File

@@ -0,0 +1,11 @@
time
getval1
setval1 = 56
getval2
setval2 = 189
getval1 = asdf
getval2 = 000000asdffd fdassdf fda
time
a
getval1
getval2

View File

@@ -0,0 +1,6 @@
########## Tutorial two ##############
add_executable(astrosib astrosib.cpp)
target_link_libraries(astrosib indidriver)

View File

@@ -0,0 +1,6 @@
INDI driver for ASTROSIB telescopes
*Write at level upper to CMakeLists.txt:*
add_subdirectory(astrosib)

View File

@@ -0,0 +1,481 @@
/*
* geany_encoding=koi8-r
* astrosib.cpp
*
* Copyright 2017 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.
*
*/
/*
INDI Developers Manual
Tutorial #2
"Simple Telescope Driver"
We develop a simple telescope simulator.
Refer to README, which contains instruction on how to build this driver, and use it
with an INDI-compatible client.
*/
/** \file simplescope.cpp
\brief Construct a basic INDI telescope device that simulates GOTO commands.
\author Jasem Mutlaq
\example simplescope.cpp
A simple GOTO telescope that simulator slewing operation.
*/
#include "astrosib.h"
#include "indicom.h"
#include <cmath>
#include <memory>
#include <string.h>
// timer polling time
#define POLLMS 1000
static const char *FOCUSER_TAB = "Focuser management";
static const char *COOLER_TAB = "Cooler management";
static const char *HEATER_TAB = "Heater management";
std::unique_ptr<AstroSib> aSib(new AstroSib());
/**************************************************************************************
** Return properties of device.
***************************************************************************************/
void ISGetProperties(const char *dev){
aSib->ISGetProperties(dev);
}
/**************************************************************************************
** Process new switch from client
***************************************************************************************/
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n){
aSib->ISNewSwitch(dev, name, states, names, n);
}
/**************************************************************************************
** Process new text from client
***************************************************************************************/
void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n){
aSib->ISNewText(dev, name, texts, names, n);
}
/**************************************************************************************
** Process new number from client
***************************************************************************************/
void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n){
aSib->ISNewNumber(dev, name, values, names, n);
}
/**************************************************************************************
** Process new blob from client
***************************************************************************************/
void ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[],
char *names[], int n){
aSib->ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
}
/**************************************************************************************
** Process snooped property from another driver
***************************************************************************************/
void ISSnoopDevice(XMLEle *root){
INDI_UNUSED(root);
}
AstroSib::AstroSib(){
/* code should be here */
//DBG_LEVEL = INDI::Logger::getInstance().addDebugLevel("Astrosib Verbose", "SCOPE");
}
AstroSib::~AstroSib(){
/* code should be here */
if (isConnected()) AstroSib::Disconnect();
}
/**************************************************************************************
** We init our properties here. The only thing we want to init are the Debug controls
***************************************************************************************/
bool AstroSib::initProperties(){
// ALWAYS call initProperties() of parent first
INDI::DefaultDevice::initProperties();
// Shutter leaves
IUFillSwitch(&ShtrsS[SHTR_OPENED], "SHTR_OPENED", "Open Scope", ISS_OFF);
IUFillSwitch(&ShtrsS[SHTR_CLOSED], "SHTR_CLOSED", "Close Scope", ISS_ON);
IUFillSwitchVector(&ShtrsSP, ShtrsS, 2, getDeviceName(), "SHTR_CONTROL", "Shutter Control",
MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 1, IPS_OK);
// Mirror and ambient temperature
IUFillNumber(&TempN[0], "MIRROR_TEMP", "Mirror temperature", "%.1f", -100., 100., 0.1, 0.);
IUFillNumber(&TempN[1], "AMBIENT_TEMP", "Ambient temperature", "%.1f", -100., 100., 0.1, 0.);
IUFillNumber(&TempN[2], "FOCUSER_TEMP", "Focuser temperature", "%.1f", -100., 100., 0.1, 0.);
IUFillNumberVector(&TempNP, TempN, 3, getDeviceName(), "TEMPERATURE", "Temperature",
MAIN_CONTROL_TAB, IP_RO, 1, IPS_IDLE);
// Focuser: current, relative and absolute position
IUFillNumber(&FocuserN[0], "FOC_CUR", "Current focuser position", "%.0f", 0, 65536, 1, 0);
IUFillNumber(&FocuserN[1], "FOC_REL", "Relative focuser position", "%.0f", -65536, 65536, 1, 0);
IUFillNumber(&FocuserN[2], "FOC_ABS", "Absolute focuser position", "%.0f", 0, 65536, 1, 0);
IUFillNumberVector(&FocuserNPcur, FocuserN, 1, getDeviceName(), "FOCUS_CUR", "Focuser current",
FOCUSER_TAB, IP_RO, 1, IPS_IDLE);
FocuserNPcur.s = IPS_OK;
IUFillNumberVector(&FocuserNP, &FocuserN[1], 2, getDeviceName(), "FOCUS_SET", "Focuser set",
FOCUSER_TAB, IP_RW, 1, IPS_IDLE);
DEBUGF(INDI::Logger::DBG_SESSION, "FOCINIT, cur: %g, rel: %g, abs: %g", FocuserN[0].value, FocuserN[1].value, FocuserN[2].value);
// Mirror cooler ON/OFF
IUFillSwitch(&CoolerS[_ON], "COOLER_ON", "Cooler ON", ISS_OFF);
IUFillSwitch(&CoolerS[_OFF], "COOLER_OFF", "Cooler OFF", ISS_ON);
IUFillSwitchVector(&CoolerSP, CoolerS, 2, getDeviceName(), "COOLER_CONTROL", "Cooler Control",
COOLER_TAB, IP_RW, ISR_1OFMANY, 1, IPS_IDLE);
IUFillSwitch(&ACoolerS[_ON], "ACOOLER_ON", "Cooler auto ON", ISS_OFF);
IUFillSwitch(&ACoolerS[_OFF], "ACOOLER_OFF", "Cooler auto OFF", ISS_ON);
IUFillSwitchVector(&ACoolerSP, ACoolerS, 2, getDeviceName(), "ACOOLER_CONTROL", "Auto Cooler Control",
COOLER_TAB, IP_RW, ISR_1OFMANY, 1, IPS_IDLE);
// Secondary mirror / Primary focus corrector Heater
IUFillSwitch(&AHeaterS[_ON], "HTR_ON", "Auto Heater ON", ISS_OFF);
IUFillSwitch(&AHeaterS[_OFF], "HTR_OFF", "Auto Heater OFF", ISS_ON);
IUFillSwitchVector(&AHeaterSP, AHeaterS, 2, getDeviceName(), "HEATER_CONTROL", "Heater Control",
HEATER_TAB, IP_RW, ISR_1OFMANY, 1, IPS_IDLE);
IUFillNumber(&PHeaterN, "PHTR", "Set heater power", "%.0f", 0, 100, 1, 0);
IUFillNumberVector(&PHeaterNP, &PHeaterN, 1, getDeviceName(), "PHTR_CTRL", "Heater power",
HEATER_TAB, IP_RW, 1, IPS_IDLE);
return true;
}
bool AstroSib::updateProperties(){
// We must ALWAYS call the parent class updateProperties() first
DefaultDevice::updateProperties();
// If we are connected, we define the property to the client.
if (isConnected()){
defineSwitch(&ShtrsSP);
defineNumber(&TempNP);
defineNumber(&FocuserNPcur);
defineNumber(&FocuserNP);
defineSwitch(&CoolerSP);
defineSwitch(&ACoolerSP);
defineSwitch(&AHeaterSP);
defineNumber(&PHeaterNP);
SetTimer(POLLMS);
// Otherwise, we delete the property from the client
}else{
deleteProperty(ShtrsSP.name);
deleteProperty(TempNP.name);
deleteProperty(FocuserNPcur.name);
deleteProperty(FocuserNP.name);
deleteProperty(CoolerSP.name);
deleteProperty(ACoolerSP.name);
deleteProperty(AHeaterSP.name);
deleteProperty(PHeaterNP.name);
}
return true;
}
/**************************************************************************************
** Process new switch from client
***************************************************************************************/
bool AstroSib::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n){
// Mak sure the call is for our device
if(!strcmp(dev,getDeviceName())){
// Check if the call for shutters
if (!strcmp(name, ShtrsSP.name)){
// Find out which state is requested by the client
const char *actionName = IUFindOnSwitchName(states, names, n);
// If door is the same state as actionName, then we do nothing. i.e. if actionName is SHTR_OPENED and our shutter is already open, we return
int currentIndex = IUFindOnSwitchIndex(&ShtrsSP);
if (!strcmp(actionName, ShtrsS[currentIndex].name)){
DEBUGF(INDI::Logger::DBG_SESSION, "Shutter is already %s", ShtrsS[currentIndex].label);
ShtrsSP.s = IPS_OK;
IDSetSwitch(&ShtrsSP, NULL);
return true;
}
// Otherwise, let us update the switch state
IUUpdateSwitch(&ShtrsSP, states, names, n);
DEBUGF(INDI::Logger::DBG_SESSION, "Shutter is going to %s", ShtrsS[currentIndex].label);
ShtrsSP.s = IPS_BUSY;
IDSetSwitch(&ShtrsSP, NULL);
return ShutterSetState(currentIndex);
}else if (!strcmp(name, CoolerSP.name)){
// Find out which state is requested by the client
const char *actionName = IUFindOnSwitchName(states, names, n);
int currentIndex = IUFindOnSwitchIndex(&CoolerSP);
CoolerSP.s = IPS_OK;
if (!strcmp(actionName, CoolerS[currentIndex].name)){
DEBUGF(INDI::Logger::DBG_SESSION, "Cooler is already %s", CoolerS[currentIndex].label);
IDSetSwitch(&CoolerSP, NULL);
return true;
}
IUUpdateSwitch(&CoolerSP, states, names, n);
currentIndex = IUFindOnSwitchIndex(&CoolerSP);
DEBUGF(INDI::Logger::DBG_SESSION, "Cooler is now %s", CoolerS[currentIndex].label);
IDSetSwitch(&CoolerSP, NULL);
return CoolerSetState(currentIndex);
}else if (!strcmp(name, ACoolerSP.name)){
// Find out which state is requested by the client
const char *actionName = IUFindOnSwitchName(states, names, n);
int currentIndex = IUFindOnSwitchIndex(&ACoolerSP);
ACoolerSP.s = IPS_OK;
if (!strcmp(actionName, ACoolerS[currentIndex].name)){
DEBUGF(INDI::Logger::DBG_SESSION, "Auto Cooler is already %s", ACoolerS[currentIndex].label);
IDSetSwitch(&ACoolerSP, NULL);
return true;
}
IUUpdateSwitch(&ACoolerSP, states, names, n);
currentIndex = IUFindOnSwitchIndex(&ACoolerSP);
DEBUGF(INDI::Logger::DBG_SESSION, "Auto Cooler is now %s", ACoolerS[currentIndex].label);
IDSetSwitch(&ACoolerSP, NULL);
return ACoolerSetState(currentIndex);
}else if (!strcmp(name, AHeaterSP.name)){
// Find out which state is requested by the client
const char *actionName = IUFindOnSwitchName(states, names, n);
int currentIndex = IUFindOnSwitchIndex(&AHeaterSP);
AHeaterSP.s = IPS_OK;
if (!strcmp(actionName, AHeaterS[currentIndex].name)){
DEBUGF(INDI::Logger::DBG_SESSION, "Auto Heater is already %s", AHeaterS[currentIndex].label);
IDSetSwitch(&AHeaterSP, NULL);
return true;
}
IUUpdateSwitch(&AHeaterSP, states, names, n);
currentIndex = IUFindOnSwitchIndex(&AHeaterSP);
DEBUGF(INDI::Logger::DBG_SESSION, "Auto Heater is now %s", AHeaterS[currentIndex].label);
IDSetSwitch(&AHeaterSP, NULL);
return AHeaterSetState(currentIndex);
}
}
// If we did not process the switch, let us pass it to the parent class to process it
return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
}
bool AstroSib::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
{
DEBUGF(INDI::Logger::DBG_SESSION, "NEW NUM, name: %s, value=%g, names = %s, n: %d", name, values[0], names[0], n);
if (dev != nullptr && strcmp(dev, getDeviceName()) == 0){
if(strcmp(name, FocuserNP.name) == 0){
if(FocInProgress){ // Already moving
DEBUG(INDI::Logger::DBG_ERROR, "Wait still focus moving ends");
return false;
}
if(IUUpdateNumber(&FocuserNP, values, names, n)){
DEBUG(INDI::Logger::DBG_ERROR, "Can't set focuser value");
FocuserN[1].value = 0;
FocuserN[2].value = focabs;
FocuserNP.s = IPS_ALERT;
IDSetNumber(&FocuserNP, nullptr);
return false;
}
DEBUGF(INDI::Logger::DBG_SESSION, "NEW NUMBER, rel: %g, abs: %g", FocuserN[1].value, FocuserN[2].value);
if(FocuserN[1].value){ // relative
focabs = foccur + FocuserN[1].value;
}else if(FocuserN[2].value){ // absolute
focabs = FocuserN[2].value;
}
if(focabs < 0){
DEBUG(INDI::Logger::DBG_ERROR, "Can't move focuser to negative values");
focabs = foccur;
FocuserNP.s = IPS_ALERT;
IDSetNumber(&FocuserNP, nullptr);
return false;
}
FocInProgress = true;
FocuserNP.s = IPS_BUSY;
FocuserN[1].value = focabs - foccur;
FocuserN[2].value = focabs;
IDSetNumber(&FocuserNP, nullptr);
return FocuserSetVal(focabs);
}else if(strcmp(name, PHeaterNP.name) == 0){
float oldval = PHeaterN.value;
if(IUUpdateNumber(&PHeaterNP, values, names, n)){
DEBUG(INDI::Logger::DBG_ERROR, "Can't set heater power value");
return false;
}
bool stat = HeaterSetPower(*values);
if(!stat) PHeaterN.value = oldval;
PHeaterNP.s = (stat) ? IPS_OK : IPS_ALERT;
IDSetNumber(&PHeaterNP, nullptr);
return stat;
}
}
return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
}
/**************************************************************************************
** INDI is asking us for our default device name
***************************************************************************************/
const char *AstroSib::getDefaultName(){
return "AstroSib telescope";
}
// Timer events
void AstroSib::TimerHit(){
static int tensecond = 0;
++tensecond;
if (isConnected()){
// once per 10 seconds check cooler & heater status
if(tensecond == 10){
CoolerChk();
HeaterChk();
tensecond = 0;
}
// check shutter if(ShtrInProgress)
if(ShtrInProgress){
ShutterChk();
}
// once per second check temperatures
TemperatureChk();
// check focuser if(FocInProgress)
if(FocInProgress){
FocuserChk();
}
SetTimer(POLLMS);
}
}
/**
* Private functions working with RS-232
*/
void AstroSib::FocuserChk(){
if(!FocInProgress){
int cur = foccur, abs = focabs, del = cur - abs;
if(del){
if(cur > abs){
cur -= 10;
if(cur < abs) cur = abs;
}else{
cur += 10;
if(cur > abs) cur = abs;
}
foccur = cur;
focabs = abs;
//DEBUGF(INDI::Logger::DBG_SESSION, "move, cur: %d, abs: %d", foccur, focabs);
}
if(foccur == focabs){
FocInProgress = false;
FocuserNP.s = IPS_IDLE;
IDSetNumber(&FocuserNPcur, "Position reached");
}else FocuserNP.s = IPS_BUSY;
}else{
FocuserNP.s = IPS_IDLE;
}
FocuserNPcur.s = IPS_OK;
FocuserN[0].value = foccur;
FocuserN[1].value = focabs - foccur;
FocuserN[2].value = focabs;
IDSetNumber(&FocuserNPcur, nullptr);
IDSetNumber(&FocuserNP, nullptr);
}
void AstroSib::TemperatureChk(){
mirrtemp += 0.01; if(mirrtemp > 20.) mirrtemp = -20.;
ambtemp += 0.05; if(ambtemp > 20.) ambtemp = -20;
TempN[0].value = mirrtemp;
TempN[1].value = ambtemp;
TempN[2].value = focusert;
TempNP.s = IPS_OK;
IDSetNumber(&TempNP, nullptr);
}
void AstroSib::ShutterChk(){
if(ShtrInProgress){
static bool closed = false;
if(closed){
ShtrsS[SHTR_OPENED].s = ISS_OFF;
ShtrsS[SHTR_CLOSED].s = ISS_ON;
}else{
ShtrsS[SHTR_OPENED].s = ISS_ON;
ShtrsS[SHTR_CLOSED].s = ISS_OFF;
}
closed = !closed;
}
ShtrInProgress = false;
ShtrsSP.s = IPS_OK;
IDSetSwitch(&ShtrsSP, NULL);
}
void AstroSib::CoolerChk(){
}
void AstroSib::HeaterChk(){
}
bool AstroSib::ShutterSetState(int newstate){
(void) newstate;
ShtrInProgress = true;
return true;
}
bool AstroSib::CoolerSetState(int newstate){
(void) newstate;
return true;
}
bool AstroSib::ACoolerSetState(int newstate){
(void) newstate;
return true;
}
bool AstroSib::AHeaterSetState(int newstate){
(void) newstate;
return true;
}
bool AstroSib::AstroSib::FocuserSetVal(int focval){
(void) focval;
return true;
}
bool AstroSib::HeaterSetPower(int pwr){
(void) pwr;
return true;
}
/**************************************************************************************
** Client is asking us to establish connection to the device
***************************************************************************************/
bool AstroSib::Connect(){
/* code should be here */
IDMessage(getDeviceName(), "Astrosib telescope connected successfully!");
SetTimer(POLLMS);
return true;
}
/**************************************************************************************
** Client is asking us to terminate connection to the device
***************************************************************************************/
bool AstroSib::Disconnect(){
/* code should be here */
IDMessage(getDeviceName(), "Astrosib telescope disconnected successfully!");
return true;
}
/**************************************************************************************
** INDI is asking us to check communication with the device via a handshake
***************************************************************************************/
bool AstroSib::Handshake(){
// When communicating with a real scope, we check here if commands are receieved
// and acknolowedged by the mount.
/* code should be here */
return true;
}

View File

@@ -0,0 +1,103 @@
/*
* geany_encoding=koi8-r
* astrosib.h
*
* Copyright 2017 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.
*
*/
/*
INDI Developers Manual
Tutorial #2
"Simple Telescope Driver"
We develop a simple telescope simulator.
Refer to README, which contains instruction on how to build this driver, and use it
with an INDI-compatible client.
*/
/** \file simplescope.h
\brief Construct a basic INDI telescope device that simulates GOTO commands.
\author Jasem Mutlaq
\example simplescope.h
A simple GOTO telescope that simulator slewing operation.
*/
#pragma once
#include "inditelescope.h"
class AstroSib : public INDI::DefaultDevice{
public:
AstroSib();
~AstroSib();
bool ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n);
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n);
void TimerHit();
protected:
bool Connect();
bool Disconnect();
bool Handshake();
const char *getDefaultName();
bool initProperties();
bool updateProperties();
private:
enum {_ON, _OFF};
// last ambient & mirror temperature values
float ambtemp {0}, mirrtemp {0}, focusert{0};
INumber TempN[3]; // mirror, ambient and focuser
INumberVectorProperty TempNP;
void TemperatureChk();
// shutter leaves state
enum {SHTR_OPENED, SHTR_CLOSED};
bool ShtrInProgress{false};
ISwitch ShtrsS[2]; // roof shutters switch
ISwitchVectorProperty ShtrsSP;
bool ShutterSetState(int newstate);
void ShutterChk();
// focuser current/relative/absolute position
bool FocInProgress{false};
int foccur{0}, focabs{0};
INumber FocuserN[3];
INumberVectorProperty FocuserNP, FocuserNPcur;
bool FocuserSetVal(int focval);
void FocuserChk();
// mirror cooler
ISwitch CoolerS[2];
ISwitchVectorProperty CoolerSP;
// cooler auto
ISwitch ACoolerS[2];
ISwitchVectorProperty ACoolerSP;
bool CoolerSetState(int newstate);
bool ACoolerSetState(int newstate);
void CoolerChk();
// heater
ISwitch AHeaterS[2];
ISwitchVectorProperty AHeaterSP;
INumber PHeaterN;
INumberVectorProperty PHeaterNP;
bool AHeaterSetState(int newstate);
bool HeaterSetPower(int pwr);
void HeaterChk();
};

View 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

View 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__

View 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 ...

View 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 дает
аж секунды! В общем, на втором этаже жизни нет.
Все-таки сделал более-менее реализацию. Работает, но нужно еще внимательно
просмотреть функцию ремаркировки: эта собака неправильно считает количество
найденных объектов!
Бульбец! Самолет задерживают на полтора часа! Буду дописывать...

View File

@@ -0,0 +1,431 @@
/*
* 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 < 1 || 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 ===================>
*/
/**
* 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;
}
CCbox *cclabel4_1(unsigned char *Img, int W, int H, int W_0, size_t *Nobj){
unsigned char *I = FC_filter(Img, W_0, H);
#include "cclabling_1.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;
}
/*
* <=================== CONNECTED COMPONENTS LABELING ===================
*/
/*
* <=================== template ===================>
*/

View File

@@ -0,0 +1,87 @@
/*
* 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
#define DBG(...) do{fprintf(stderr, __VA_ARGS__);}while(0)
#else
#define DBG(...)
#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 *cclabel4_1(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__

View File

@@ -0,0 +1,210 @@
/*
* 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, w = W-1;
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;
}
#else
// no neighbour down & neighbour to the right -> hermit
if((y < h && ptr[W] == 0) && (x < w && ptr[1] == 0)){
*ptr = 0; // erase this hermit!
DBG("hermit!\n");
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);
printf("%6.4f\t%zd\n", dtime()-t0, Ncur);

View File

@@ -0,0 +1,168 @@
/*
* 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
Nmax = W*H/4; // max number of labels
size_t *labels = char2st(I, W, H, W_0);
int w = W - 1, h = H - 1;
//printf("time for char2st: %gs\n", dtime()-t0);
int y;
size_t *assoc = Malloc(Nmax, sizeof(size_t)); // allocate memory for "remark" array
size_t last_assoc_idx = 0; // last index filled in assoc array
size_t currentnum = 0; // current pixel number
inline void remark(size_t old, size_t new){ // remark in assoc[] pixel with value old to assoc[new] or vice versa
size_t New = assoc[new], Old = assoc[old];
if(Old == New){
DBG("components equal (%zd->%zd and %zd->%zd)\n", old, Old, new, New);
return;
}
DBG("N=%zd, curr=%zd, old:%zd->%zd <-> new:%zd->%zd ", N, currentnum, old, Old, new, New);
// now we must check Old: if Old<New we should swap them
if(Old < New){
register size_t _tmp_ = Old; Old = New; New = _tmp_; // swap values
_tmp_ = old; old = new; new = _tmp_;
}
// decrement counters for current value (because we make merging)
--currentnum;
//OMP_FOR()
for(size_t i = 1; i < last_assoc_idx; ++i){
size_t m = assoc[i];
DBG("assoc[%zd]=%zd ", i, m);
if(m < Old) continue; // lower values
if(m == Old){ // change all old markers to new
assoc[i] = New;
DBG("remark %zd->%zd to ->%zd ", i, m, New);
}else{ // decrement all higher values
DBG("decr %zd->%zd, ", i, m);
assoc[i]--;
}
}
DBG("\n");
}
//t0 = dtime();
size_t *ptr = labels;
for(y = 0; y < H; y++){
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
DBG("line #%d (w = %d) ", y, h);
#ifdef LABEL_8 // check, whether pixel is not single
size_t *D = (y < h) ? &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!
DBG("hermit!\n");
continue;
}
#else
// no neighbour down & neighbour to the right -> hermit
if((y < h && ptr[W] == 0) && (x < w && ptr[1] == 0)){
*ptr = 0; // erase this hermit!
DBG("hermit!\n");
continue;
}
#endif
upmark = ++N;
assoc[upmark] = ++currentnum; // refresh "assoc"
DBG("assoc[%zd] = %zd\n", upmark, currentnum);
last_assoc_idx = upmark + 1;
}
*ptr = upmark;
curmark = 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, currentnum);
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);
printf("%6.4f\t%zd\n", dtime()-t0, currentnum);

View 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++;
}

View 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++;
}

View File

View File

@@ -0,0 +1,114 @@
/*
* 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 <math.h>
#include <stdio.h>
#include "binmorph.h"
// these includes are for randini
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.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 = 500, H = 500, 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));
srand48(throw_random_seed());
for(i = 0; i < H; i++)
for(j = 0; j < W; j++)
inp[i*W+j] = (drand48() > 0.7);
unsigned char *b2ch = bool2char(inp, W, H, &W_0);//, *immer;
/*printf("image:\n");
printC(b2ch, W_0, H);*/
// 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 4-connected components:\n");
printf("4new\t");
cclabel4_1(b2ch, W, H, W_0, &NL);
// printf("\nmark 4-connected components (old):\n");
printf("4old\t");
cclabel4(b2ch, W, H, W_0, &NL);
// printf("\nmark 8-connected components:\n");
printf("8new\t");
cclabel8_1(b2ch, W, H, W_0, &NL);
// printf("\nmark 8-connected components (old):\n");
printf("8old\t");
cclabel8(b2ch, W, H, W_0, &NL);
FREE(b2ch);
return 0;
}
/*
* bench
* names = ["4new"; "4old"; "8new"; "8old"]; for i = 1:4; nm = names(i,:); time = []; sz = []; for i = 1:52; if(name{i} == nm) time = [time speed(i)]; sz = [sz num(i)]; endif;endfor; ts = time./sz; printf("%s: time/object = %.1f +- %.1f us\n", nm, mean(ts)*1e6, std(ts)*1e6);endfor;
*
* 0.25 megapixels
* 4new: time/object = 1.9 +- 0.4 us
* 4old: time/object = 1.7 +- 0.1 us
* 8new: time/object = 3.9 +- 0.1 us
* 8old: time/object = 13.5 +- 2.1 us
*
* 1 megapixels ---> [name speed num] = textread("1megapixel", "%s %f %f");
* 4new: time/object = 5.5 +- 0.5 us
* 4old: time/object = 5.3 +- 0.0 us
* 8new: time/object = 13.3 +- 0.1 us
* 8old: time/object = 47.6 +- 2.2 us
*
* 4 megapixels ---> [name speed num] = textread("4megapixels", "%s %f %f");
* 4new: time/object = 21.3 +- 1.5 us
* 4old: time/object = 22.3 +- 2.2 us
* 8new: time/object = 57.6 +- 1.3 us
* 8old: time/object = 195.5 +- 11.7 us
*
*
*/

129
_deprecated/fifo_lifo.c Normal file
View 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
_deprecated/fifo_lifo.h Normal file
View 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__

View 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

View File

@@ -0,0 +1,22 @@
PROGRAM = myopt_example
LDFLAGS = -lm
SRCS = $(wildcard *.c)
CC = gcc
DEFINES = -D_XOPEN_SOURCE=1111
CXX = gcc
CFLAGS = -Wall -Werror -Wextra $(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 $(CXX.SRCS)

View File

@@ -0,0 +1,264 @@
/*
* cmdlnopts.c - the only function that parse 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 <assert.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <math.h>
#include "cmdlnopts.h"
#define RAD 57.2957795130823
#define D2R(x) ((x) / RAD)
#define R2D(x) ((x) * RAD)
/*
* here are global parameters initialisation
*/
int help;
glob_pars G;
mirPar M;
int rewrite_ifexists = 0, // rewrite existing files == 0 or 1
verbose = 0; // each -v increments this value, e.g. -vvv sets it to 3
// DEFAULTS
// default global parameters
glob_pars const Gdefault = {
NULL, // size of initial array of surface deviations
100, // size of interpolated S0
1000, // resulting image size
10000, // amount of photons falled to one pixel of S1 by one iteration
0, // add to mask random numbers
NULL, // amplitude of added random noice
NULL, // mirror
0, // rest num
NULL, // rest pars
NULL, // str
NULL // filter
};
//default mirror parameters
mirPar const Mdefault = {
6., // diameter
24.024, // focus
0., // inclination from Z axe (radians)
0. // azimuth of inclination (radians)
};
// here are functions & definitions for complex parameters (with suboptions etc)
bool get_mir_par(void *arg);
const char MirPar[] = "set mirror parameters, arg=[diam=num:foc=num:Zincl=ang:Aincl=ang]\n" \
"\t\t\tALL DEGREES ARE IN FORMAT [+-][DDd][MMm][SS.S] like -10m13.4 !\n" \
"\t\tdiam - diameter of mirror\n" \
"\t\tfoc - mirror focus ratio\n" \
"\t\tZincl - inclination from Z axe\n" \
"\t\tAincl - azimuth of inclination";
const char FilPar[] = "set filter parameters, arg=[type=type:xsz=num:ysz=num]\n" \
"\t\ttype - filter type(med, lap, lg)\n" \
"\t\txsz,ysz - area size (default is 3x3)";
/*
* Define command line options by filling structure:
* name has_arg flag val type argptr help
*/
myoption cmdlnopts[] = {
// set 1 to param despite of its repeating number:
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
// simple integer parameter with obligatory arg:
{"int-size",NEED_ARG, NULL, 'i', arg_int, APTR(&G.S_interp), "size of interpolated array of surface deviations"},
{"image-size",NEED_ARG,NULL, 'I', arg_int, APTR(&G.S_image), "resulting image size"},
{"N-photons",NEED_ARG,NULL, 'N', arg_int, APTR(&G.N_phot), "amount of photons falled to one pixel of matrix by one iteration"},
// parameter with suboptions parsed directly in parseargs
{"mir-parameters",NEED_ARG,NULL,'M', arg_function,APTR(&get_mir_par),MirPar},
// another variant of setting parameter without argument
{"add-noice",NO_ARGS, &G.randMask,1, arg_none, NULL, "add random noice to mirror surface deviations"},
{"force", NO_ARGS, &rewrite_ifexists,1,arg_none,NULL, "rewrite output file if exists"},
// incremented parameter without args (any -v will increment value of "verbose")
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&verbose), "verbose level (each -v increase it)"},
// integer parameter which can occur several times:
{"dev-size",MULT_PAR, NULL, 'd', arg_int, APTR(&G.S_dev), "size of initial array of surface deviations"},
// double array
{"noice-amp",MULT_PAR, NULL, 'a', arg_double, APTR(&G.randAmp), "amplitude of random noice (default: 1e-8)"},
// string array
{"str", MULT_PAR, NULL, 's', arg_string, APTR(&G.str), "some string parameters (may meets many times)"},
// suboptions array that will be parsed outside (the same as string array):
{"filter", MULT_PAR, NULL, 'f', arg_string, APTR(&G.filter), FilPar},
end_option
};
// suboptions structure for get_mirpars
// in array last MUST BE {0,0,0}
typedef struct{
double *val; // pointer to result
char *par; // parameter name (CASE-INSENSITIVE!)
bool isdegr; // == TRUE if parameter is an angle in format "[+-][DDd][MMm][SS.S]"
} suboptions;
/**
* Safely convert data from string to double
*
* @param num (o) - double number read from string
* @param str (i) - input string
* @return TRUE if success
*/
bool myatod(double *num, const char *str){
double res;
char *endptr;
assert(str);
res = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0'){
printf("Wrong double number format!");
return FALSE;
}
*num = res;
return TRUE;
}
/**
* Convert string "[+-][DDd][MMm][SS.S]" into radians
*
* @param ang (o) - angle in radians or exit with help message
* @param str (i) - string with angle
* @return TRUE if OK
*/
bool get_radians(double *ret, char *str){
double val = 0., ftmp, sign = 1.;
char *ptr;
assert(str);
switch(*str){ // check sign
case '-':
sign = -1.;
case '+':
str++;
}
if((ptr = strchr(str, 'd'))){ // found DDD.DDd
*ptr = 0; if(!myatod(&ftmp, str)) return FALSE;
ftmp = fabs(ftmp);
if(ftmp > 360.){
printf("Degrees should be less than 360");
return FALSE;
}
val += ftmp;
str = ptr + 1;
}
if((ptr = strchr(str, 'm'))){ // found DDD.DDm
*ptr = 0; if(!myatod(&ftmp, str)) return FALSE;
ftmp = fabs(ftmp);
val += ftmp / 60.;
str = ptr + 1;
}
if(strlen(str)){ // there is something more
if(!myatod(&ftmp, str)) return FALSE;
ftmp = fabs(ftmp);
val += ftmp / 3600.;
}
*ret = D2R(val * sign); // convert degrees to radians
return TRUE;
}
/**
* 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_mirpars(void *arg, suboptions *V){
char *tok, *val, *par;
int i;
tok = strtok(arg, ":,");
do{
if((val = strchr(tok, '=')) == NULL){ // wrong format
printf("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;
}else{ // simple double
if(!myatod(V[i].val, val)) // wrong number
return FALSE;
}
break;
}
}
if(!V[i].val){ // nothing found - wrong format
printf("Bad keyword!");
return FALSE;
}
}while((tok = strtok(NULL, ":,")));
return TRUE;
}
/**
* functions of subargs parsing can looks as this
*/
/**
* Parse string of mirror parameters (--mir-diam=...)
*
* @param arg (i) - string with description
* @return TRUE if success
*/
bool get_mir_par(void *arg){
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},
{0,0,0}
};
return get_mirpars(arg, V);
}
/**
* Parse 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 *parse_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
parseargs(&argc, &argv, cmdlnopts);
if(help) showhelp(-1, cmdlnopts);
if(argc > 0){
G.rest_pars_num = argc;
G.rest_pars = calloc(argc, sizeof(char*));
for (i = 0; i < argc; i++)
G.rest_pars[i] = strdup(argv[i]);
}
return &G;
}

Some files were not shown because too many files have changed in this diff Show More