add median filter

This commit is contained in:
Edward Emelianov 2021-08-24 12:19:52 +03:00
parent 9d76c96602
commit 4ef3ae7605
12 changed files with 206 additions and 126 deletions

View File

@ -285,10 +285,10 @@ static int setgain(_U_ float e){
static int changeformat(frameformat *fmt){
FNAME();
if(!isopened) return FALSE;
if(!setInt("Width", fmt->w)) return FALSE;
if(!setInt("Height", fmt->h)) return FALSE;
if(!setInt("OffsetX", fmt->xoff)) return FALSE;
if(!setInt("OffsetY", fmt->yoff)) return FALSE;
setInt("Width", fmt->w);
setInt("Height", fmt->h);
setInt("OffsetX", fmt->xoff);
setInt("OffsetY", fmt->yoff);
int64_values i;
if(getInt("Width", &i)) fmt->w = i.val;
if(getInt("Height", &i)) fmt->h = i.val;

View File

@ -28,6 +28,7 @@
#include "grasshopper.h"
#include "imagefile.h"
#include "improc.h"
#include "median.h"
// pointer to selected camera
static camera *theCam = NULL;
@ -102,16 +103,17 @@ void camdisconnect(){
static void calcexpgain(float newexp){
DBG("recalculate exposition: oldexp=%g, oldgain=%g, newexp=%g", exptime, gain, newexp);
if(newexp*1.25 > theconf.minexp){
if(gain < gainmax - 1.){ // increase gain first
while(newexp*1.25 > theconf.minexp){ // increase gain first
if(gain < gainmax - 0.9999){
gain += 1.;
newexp /= 1.25;
}else break;
}
}else{ // make gain lower
if(1.25*newexp < theconf.maxexp && gain > 1.){
while(newexp < theconf.minexp){
if(1.25*newexp < theconf.maxexp && gain > 0.9999){
gain -= 1.;
newexp *= 1.25;
}
}else break;
}
if(newexp < theconf.minexp) newexp = theconf.minexp;
else if(newexp > theconf.maxexp) newexp = theconf.maxexp;
@ -155,12 +157,12 @@ static void recalcexp(Image *I){
if(sum100 > 100) break;
}
DBG("Sum100=%d, idx100=%d", sum100, idx100);
if(idx100 > 200 && idx100 < 250) return; // good values
if(idx100 > 250){ // exposure too long
if(idx100 > 230 && idx100 < 253) return; // good values
if(idx100 > 253){ // exposure too long
calcexpgain(0.7*exptime);
}else{ // exposure too short
if(idx100 > 5)
calcexpgain(exptime * 210. / (float)idx100);
calcexpgain(exptime * 230. / (float)idx100);
else
calcexpgain(exptime * 50.);
}
@ -233,6 +235,14 @@ int camcapture(void (*process)(Image*)){
brightness = theconf.brightness;
}
if(process){
if(theconf.medfilt){
Image *X = get_median(oIma, theconf.medseed);
if(X){
FREE(oIma->data);
FREE(oIma);
oIma = X;
}
}
process(oIma);
}
FREE(oIma->data);

View File

@ -56,7 +56,8 @@ configuration theconf = {
.minexp=EXPOS_MIN - DBL_EPSILON,
.fixedexp=EXPOS_MIN,
.gain = 20.,
.intensthres=DEFAULT_INTENSTHRES
.intensthres=DEFAULT_INTENSTHRES,
.medseed=MIN_MEDIAN_SEED,
};
// {"", PAR_DOUBLE, (void*)&theconf., 0},
@ -125,6 +126,14 @@ static confparam parvals[] = {
"brightness value"},
{"starssort", PAR_INT, (void*)&theconf.starssort, 0, -DBL_EPSILON, 1.+DBL_EPSILON,
"stars sorting algorithm: by distance from target (0) or by intensity (1)"},
{"medfilt", PAR_INT, (void*)&theconf.medfilt, 0, -DBL_EPSILON, 1.+DBL_EPSILON,
"use median filter"},
{"medseed", PAR_INT, (void*)&theconf.medseed, 0, MIN_MEDIAN_SEED-DBL_EPSILON, MAX_MEDIAN_SEED+DBL_EPSILON,
"median filter radius"},
{"fixedbg", PAR_INT, (void*)&theconf.fixedbkg, 0, -DBL_EPSILON, 1.+DBL_EPSILON,
"don't calculate background, use fixed value instead"},
{"fbglevel", PAR_INT, (void*)&theconf.fixedbkgval, 0, FIXED_BK_MIN-DBL_EPSILON, FIXED_BK_MAX+DBL_EPSILON,
"fixed background level"},
{NULL, 0, NULL, 0, 0., 0., NULL}
};

View File

@ -48,6 +48,12 @@
#define KUVMAX (5000.)
// default coefficient for corrections (move to Kdu, Kdv instead of du, dv)
#define KCORR (0.97)
// min/max median seed
#define MIN_MEDIAN_SEED (1)
#define MAX_MEDIAN_SEED (7)
// fixed background
#define FIXED_BK_MIN (0)
#define FIXED_BK_MAX (250)
// exposition methods: 0 - auto, 1 - fixed
#define EXPAUTO (0)
@ -78,6 +84,10 @@ typedef struct{
int stpserverport; // steppers' server port
int starssort; // stars sorting algorithm: by distance from target (0) or by intensity (1)
int expmethod; // 0 - auto, 1 - fixed
int medfilt; // == 1 to make median filter before calculations
int medseed; // median seed
int fixedbkg; // don't calculate background, use fixed value instead
int fixedbkgval; // value of bk
// dU = Kxu*dX + Kyu*dY; dV = Kxv*dX + Kyv*dY
double Kxu; double Kyu;
double Kxv; double Kyv;

View File

@ -128,18 +128,25 @@ Image *u8toImage(uint8_t *data, int width, int height, int stride){
outp->width = width;
outp->height = height;
outp->dtype = FLOAT_IMG;
uint8_t min = *data, max = min;
for(int y = 0; y < height; ++y){
uint8_t *ptr = &data[y*stride];
for(int x = 0; x < width; ++x){
uint8_t p = *ptr++;
if(p < min) min = p;
else if(p > max) max = p;
/*
int histogram[256] = {0};
int wh = width*height;
#pragma omp parallel
{
int histogram_private[256] = {0};
#pragma omp for nowait
for(int i = 0; i < wh; ++i){
++histogram_private[data[i]];
}
#pragma omp critical
{
for(int i=0; i<256; ++i) histogram[i] += histogram_private[i];
}
}
outp->minval = (Imtype) min;
outp->maxval = (Imtype) max;
//DBG("\nMAX=%g, MIN=%g\n", outp->maxval, outp->minval);
red("HISTO:\n");
for(int i = 0; i < 256; ++i) printf("%d:\t%d\n", i, histogram[i]);
*/
outp->data = MALLOC(Imtype, width*height);
// flip image updown for FITS coordinate system
OMP_FOR()
@ -150,6 +157,7 @@ Image *u8toImage(uint8_t *data, int width, int height, int stride){
*Out++ = (Imtype)(*In++);
}
}
Image_minmax(outp);
return outp;
}

View File

@ -38,27 +38,16 @@
volatile atomic_ullong ImNumber = 0; // GLOBAL: counter of processed images
volatile atomic_bool stopwork = FALSE; // GLOBAL: suicide
//int autoExposition = 1; // GLOBAL: ==1 if exposition calculation is auto
// GLOBAL: function to get stepper server status
char *(*stepstatus)(const char *messageid, char *buf, int buflen) = NULL;
// GLOBAL: set new status
char *(*setstepstatus)(const char *newstatus, char *buf, int buflen) = NULL;
// GLOBAL: move focus
char *(*movefocus)(const char *newstatus, char *buf, int buflen) = NULL;
// GLOBAL: get image information
char *(*imagedata)(const char *messageid, char *buf, int buflen);
// GLOBAL: disconnect stepper server
void (*stepdisconnect)() = NULL;
char *(*imagedata)(const char *messageid, char *buf, int buflen) = NULL;
steppersproc *theSteppers = NULL;
static FILE *fXYlog = NULL;
static double tstart = 0.; // time of logging start
static double FPS = 0.; // frames per second
// function to process calculated corrections
static void (*proc_corr)(double, double) = NULL;
typedef struct{
uint32_t area; // object area in pixels
double Isum; // total object's intensity over background
@ -150,16 +139,18 @@ static void getDeviation(object *curobj){
xx /= theconf.naverage; yy /= theconf.naverage;
Sx = sqrt(xsum2/theconf.naverage - xx*xx);
Sy = sqrt(ysum2/theconf.naverage - yy*yy);
green("\n\n\n Average centroid: X=%g (+-%g), Y=%g (+-%g)\n", xx, Sx, yy, Sy);
#ifdef EBUG
green("\n Average centroid: X=%g (+-%g), Y=%g (+-%g)\n", xx, Sx, yy, Sy);
#endif
LOGDBG("getDeviation(): Average centroid: X=%g (+-%g), Y=%g (+-%g)", xx, Sx, yy, Sy);
averflag = 1;
if(fXYlog) fprintf(fXYlog, "%.1f\t%.1f\t%.1f\t%.1f", xx, yy, Sx, Sy);
process_corrections:
if(proc_corr && averflag){
if(theSteppers && theSteppers->proc_corr && averflag){
if(Sx > XY_TOLERANCE || Sy > XY_TOLERANCE){
LOGDBG("Bad value - not process"); // don't run processing for bad data
}else
proc_corr(xx, yy);
theSteppers->proc_corr(xx, yy);
}
XYnewline();
}
@ -248,21 +239,24 @@ void process_file(Image *I){
};
}
DELTA("Labeling");
printf("T%zd, N=%d\n", time(NULL), objctr);
DBG("T%zd, N=%d\n", time(NULL), objctr);
if(objctr > 1){
if(theconf.starssort)
qsort(Objects, objctr, sizeof(object), compIntens);
else
qsort(Objects, objctr, sizeof(object), compDist);
}
#ifdef EBUG
object *o = Objects;
green("%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\n",
"N", "Area", "Mv", "W/H", "Xc", "Yc", "Sx", "Sy");
green("%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%8s\n",
"N", "Area", "Mv", "W/H", "Xc", "Yc", "Sx", "Sy", "Area/r^2");
for(int i = 0; i < objctr; ++i, ++o){
// 1.0857 = 2.5/ln(10)
printf("%6d\t%6d\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\n",
i, o->area, 20.-1.0857*log(o->Isum), o->WdivH, o->xc, o->yc, o->xsigma, o->ysigma);
printf("%6d\t%6d\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%8.1f\n",
i, o->area, 20.-1.0857*log(o->Isum), o->WdivH, o->xc, o->yc,
o->xsigma, o->ysigma, o->area/o->xsigma/o->ysigma);
}
#endif
getDeviation(Objects); // calculate dX/dY and process corrections
{ // prepare image and save jpeg
uint8_t *outp = NULL;
@ -381,11 +375,7 @@ void setpostprocess(const char *name){
WARNX("Pusiserver unavailable, will check later");
LOGWARN("Pusiserver unavailable, will check later");
}
stepdisconnect = pusi_stop;
proc_corr = pusi_process_corrections;
stepstatus = pusi_status;
setstepstatus = set_pusistatus;
movefocus = set_pfocus;
theSteppers = &pusyCANbus;
}else{
WARNX("Unknown postprocess \"%s\"", name);
LOGERR("Unknown postprocess \"%s\"", name);

View File

@ -32,12 +32,20 @@
extern volatile atomic_bool stopwork;
extern volatile atomic_ullong ImNumber;
//extern int autoExposition;
extern char *(*stepstatus)(const char *messageid, char *buf, int buflen);
extern char *(*setstepstatus)(const char *newstatus, char *buf, int buflen);
extern char *(*movefocus)(const char *newstatus, char *buf, int buflen);
extern char *(*imagedata)(const char *messageid, char *buf, int buflen);
extern void (*stepdisconnect)();
typedef struct{
void (*proc_corr)(double, double);
char *(*stepstatus)(const char *messageid, char *buf, int buflen);
char *(*setstepstatus)(const char *newstatus, char *buf, int buflen);
char *(*movefocus)(const char *newstatus, char *buf, int buflen);
char *(*moveByU)(const char *val, char *buf, int buflen);
char *(*moveByV)(const char *val, char *buf, int buflen);
void (*stepdisconnect)();
} steppersproc;
extern steppersproc* theSteppers;
void process_file(Image *I);
int process_input(InputType tp, char *name);

View File

@ -46,7 +46,7 @@ void signals(int sig){
DBG("unlink(GP->pidfile)");
unlink(GP->pidfile);
}
if(stepdisconnect) stepdisconnect();
if(theSteppers && theSteppers->stepdisconnect) theSteppers->stepdisconnect();
DBG("closeXYlog()");
closeXYlog();
DBG("EXIT %d", sig);

View File

@ -28,6 +28,7 @@
#include <string.h>
#include <usefull_macros.h>
#include "config.h"
#include "imagefile.h"
#include "median.h"
@ -390,8 +391,8 @@ Image *get_median(const Image *img, int seed){
* @retur 0 if error
*/
int get_stat(const Image *in, int seed, Image **mean, Image **std){
if(!in) return 0;
if(seed < 1 || seed > (in->width - 1)/2 || seed > (in->height - 1)/2) return 0;
if(!in) return FALSE;
if(seed < 1 || seed > (in->width - 1)/2 || seed > (in->height - 1)/2) return FALSE;
#ifdef EBUG
double t0 = dtime();
#endif
@ -434,7 +435,7 @@ int get_stat(const Image *in, int seed, Image **mean, Image **std){
*std = S;
}
DBG("time for mean/sigma computation: %gs", dtime() - t0);
return 1;
return TRUE;
}
/**
@ -445,14 +446,19 @@ int get_stat(const Image *in, int seed, Image **mean, Image **std){
*/
int calc_background(Image *img, Imtype *bk){
//DBG("image: min=%g, max=%g", img->minval, img->maxval);
if(!img || !bk) return FALSE;
if(img->maxval - img->minval < DBL_EPSILON){
WARNX("Zero image!");
WARNX("Zero or overilluminated image!");
return FALSE;
}
if(theconf.fixedbkg){
*bk = theconf.fixedbkg;
return TRUE;
}
int w = img->width, h = img->height, wh = w*h;
Imtype min = img->minval, ampl = img->maxval - min;
int histogram[256] = {0};
//DBG("min: %g, max: %g, ampl: %g", min, img->maxval, ampl);
DBG("min: %g, max: %g, ampl: %g", min, img->maxval, ampl);
#pragma omp parallel
{
int histogram_private[256] = {0};
@ -492,8 +498,8 @@ int calc_background(Image *img, Imtype *bk){
//borderidx = (borderidx + modeidx) / 2;
Imtype borderval = ((Imtype)borderidx / 255.)*ampl + min;
if(bk) *bk = borderval;
// green("HISTO:\n");
// for(int i = 0; i < 256; ++i) printf("%d:\t%d\t%d\n", i, histogram[i], diff2[i]);
//green("HISTO:\n");
//for(int i = 0; i < 256; ++i) printf("%d:\t%d\t%d\n", i, histogram[i], diff2[i]);
// calculate values of upper 2% border
#if 0
Image *out = Image_sim(img);

View File

@ -114,6 +114,7 @@ static pthread_mutex_t sendmesg_mutex = PTHREAD_MUTEX_INITIALIZER;
// current steps counters (zero at the middle)
static volatile atomic_int Uposition = 0, Vposition = 0, Fposition = 0;
static volatile atomic_int dUmove = 0, dVmove = 0;
static volatile atomic_bool Umoving = FALSE, Vmoving = FALSE, Fmoving = FALSE;
static uint8_t fixerr = 0; // ==1 if can't fixed
@ -358,14 +359,14 @@ int pusi_connect(){
}
// stop processing & disconnect
void pusi_stop(){
static void pusi_stop(){
pthread_join(processingthread, NULL);
pusi_disconnect();
}
// return TRUE if motor is stopped
static int moving_finished(const char *mesgstatus, volatile atomic_int *position){
double val;
double val = 0.;
char *ans = NULL;
int ret = TRUE;
if(send_message(mesgstatus, &ans) && getparval(PARstatus, ans, &val)){
@ -608,7 +609,7 @@ static int try2correct(double dX, double dY){
* @param X, Y - centroid (x,y) in screen coordinate system
* This function called from improc.c each time the corrections calculated (ONLY IF Xtarget/Ytarget > -1)
*/
void pusi_process_corrections(double X, double Y){
static void pusi_process_corrections(double X, double Y){
static bool coordstrusted = TRUE;
if(ismoving){ // don't process coordinates when moving
coordstrusted = FALSE;
@ -644,7 +645,7 @@ static int pusi_setstate(pusistate newstate){
// get current status (global variable stepstatus)
// return JSON string with different parameters
char *pusi_status(const char *messageid, char *buf, int buflen){
static char *pusi_status(const char *messageid, char *buf, int buflen){
int l;
char *bptr = buf;
const char *s = NULL, *stage = NULL;
@ -723,7 +724,7 @@ typedef struct{
pusistate state;
} strstate;
// commands from client to change status
strstate stringstatuses[] = {
static strstate stringstatuses[] = {
{"disconnect", PUSI_DISCONN},
{"relax", PUSI_RELAX},
{"setup", PUSI_SETUP},
@ -734,7 +735,7 @@ strstate stringstatuses[] = {
};
// try to set new status (global variable stepstatus)
char *set_pusistatus(const char *newstatus, char *buf, int buflen){
static char *set_pusistatus(const char *newstatus, char *buf, int buflen){
strstate *s = stringstatuses;
pusistate newstate = PUSI_UNDEFINED;
while(s->str){
@ -766,19 +767,6 @@ char *set_pusistatus(const char *newstatus, char *buf, int buflen){
return buf;
}
// change focus (global variable movefocus)
char *set_pfocus(const char *newstatus, char *buf, int buflen){
int newval = atoi(newstatus);
if(newval < theconf.minFpos || newval > theconf.maxFpos){
snprintf(buf, buflen, FAIL);
}else{
snprintf(buf, buflen, OK);
newfocpos = newval;
chfocus = TRUE;
}
return buf;
}
// MAIN THREAD
static void *pusi_process_states(_U_ void *arg){
FNAME();
@ -786,10 +774,11 @@ static void *pusi_process_states(_U_ void *arg){
while(!stopwork){
usleep(10000);
// check for moving
switch(state){
case PUSI_SETUP:
case PUSI_GOTOTHEMIDDLE:
case PUSI_FIX:
if(state == PUSI_DISCONN){
sleep(1);
pusi_connect_server();
continue;
}
if(moving_finished(Ustatus, &Uposition)) Umoving = FALSE;
else Umoving = TRUE;
if(moving_finished(Vstatus, &Vposition)) Vmoving = FALSE;
@ -798,14 +787,6 @@ static void *pusi_process_states(_U_ void *arg){
else Fmoving = TRUE;
if(Umoving || Vmoving || Fmoving) ismoving = TRUE;
else ismoving = FALSE;
break;
case PUSI_DISCONN:
sleep(1);
pusi_connect_server();
break;
default:
break;
}
if(ismoving){
coordsRdy = FALSE;
continue;
@ -817,6 +798,16 @@ static void *pusi_process_states(_U_ void *arg){
moveF(delta); moveU(delta); moveV(delta);
continue;
}
if(dUmove){
moveU(dUmove);
dUmove = 0;
continue;
}
if(dVmove){
moveV(dVmove);
dVmove = 0;
continue;
}
// if we are here, all U/V/F moving is finished
if(state != PUSI_DISCONN) first = TRUE;
switch(state){ // pusirobo state machine
@ -871,3 +862,47 @@ static void *pusi_process_states(_U_ void *arg){
}
return NULL;
}
// change focus (global variable movefocus)
static char *set_pfocus(const char *newstatus, char *buf, int buflen){
int newval = atoi(newstatus);
if(newval < theconf.minFpos || newval > theconf.maxFpos){
snprintf(buf, buflen, FAIL);
}else{
snprintf(buf, buflen, OK);
newfocpos = newval;
chfocus = TRUE;
}
return buf;
}
// move by U and V axis
static char *Umove(const char *val, char *buf, int buflen){
int d = atoi(val);
int Unfixed = Uposition + d + Fposition;
if(Unfixed > theconf.maxUsteps || Unfixed < -theconf.maxUsteps){
snprintf(buf, buflen, FAIL);
}
dUmove = d;
snprintf(buf, buflen, OK);
return buf;
}
static char *Vmove(const char *val, char *buf, int buflen){
int d = atoi(val);
int Vnfixed = Vposition + d + Fposition;
if(Vnfixed > theconf.maxVsteps || Vnfixed < -theconf.maxVsteps){
snprintf(buf, buflen, FAIL);
}
dVmove = d;
snprintf(buf, buflen, OK);
return buf;
}
steppersproc pusyCANbus = {
.stepdisconnect = pusi_stop,
.proc_corr = pusi_process_corrections,
.stepstatus = pusi_status,
.setstepstatus = set_pusistatus,
.movefocus = set_pfocus,
.moveByU = Umove,
.moveByV = Vmove,
};

View File

@ -19,17 +19,10 @@
#ifndef PUSIROBO_H__
#define PUSIROBO_H__
#include "improc.h"
extern steppersproc pusyCANbus;
// try to connect to local pusirobo server
int pusi_connect();
// disconnect
void pusi_stop();
// global variable proc_corr
void pusi_process_corrections(double X, double Y);
// global variable stepstatus
char *pusi_status(const char *messageid, char *buf, int buflen);
// global variable setstepstatus
char *set_pusistatus(const char *newstatus, char *buf, int buflen);
// global variable movefocus
char *set_pfocus(const char *newstatus, char *buf, int buflen);
#endif // PUSIROBO_H__

View File

@ -74,14 +74,21 @@ static getter getterHandlers[] = {
static char *setstepperstate(const char *state, char *buf, int buflen);
static char *setfocusstate(const char *state, char *buf, int buflen);
//static char *setexposition(const char *expos, char *buf, int buflen);
//static char *setexposmethod(const char *expos, char *buf, int buflen);
static char *moveU(const char *val, char *buf, int buflen);
static char *moveV(const char *val, char *buf, int buflen);
static setter setterHandlers[] = {
{"stpstate", setstepperstate, "Set given steppers' server state"},
{"focus", setfocusstate, "Move focus to given value"},
{"moveU", moveU, "Relative moving by U axe"},
{"moveV", moveV, "Relative moving by V axe"},
{NULL, NULL, NULL}
};
static char *retFAIL(char *buf, int buflen){
snprintf(buf, buflen, FAIL);
return buf;
}
/**************** functions to process commands ****************/
// getters
static char *helpmsg(_U_ const char *messageid, char *buf, int buflen){
@ -107,28 +114,32 @@ static char *helpmsg(_U_ const char *messageid, char *buf, int buflen){
return NULL;
}
static char *stepperstatus(const char *messageid, char *buf, int buflen){
if(stepstatus) return stepstatus(messageid, buf, buflen);
snprintf(buf, buflen, FAIL);
return buf;
if(theSteppers && theSteppers->stepstatus) return theSteppers->stepstatus(messageid, buf, buflen);
return retFAIL(buf, buflen);
}
static char *getimagedata(const char *messageid, char *buf, int buflen){
if(imagedata) return imagedata(messageid, buf, buflen);
else snprintf(buf, buflen, FAIL);
return buf;
return retFAIL(buf, buflen);
}
// setters
static char *setstepperstate(const char *state, char *buf, int buflen){
DBG("set steppersstate to %s", state);
if(setstepstatus) return setstepstatus(state, buf, buflen);
snprintf(buf, buflen, FAIL);
return buf;
if(theSteppers && theSteppers->setstepstatus) return theSteppers->setstepstatus(state, buf, buflen);
return retFAIL(buf, buflen);
}
static char *setfocusstate(const char *state, char *buf, int buflen){
DBG("move focus to %s", state);
if(movefocus) return movefocus(state, buf, buflen);
snprintf(buf, buflen, FAIL);
return buf;
if(theSteppers && theSteppers->movefocus) return theSteppers->movefocus(state, buf, buflen);
return retFAIL(buf, buflen);
}
static char *moveU(const char *val, char *buf, int buflen){
if(theSteppers && theSteppers->moveByU) return theSteppers->moveByU(val, buf, buflen);
return retFAIL(buf, buflen);
}
static char *moveV(const char *val, char *buf, int buflen){
if(theSteppers && theSteppers->moveByV) return theSteppers->moveByU(val, buf, buflen);
return retFAIL(buf, buflen);
}
/*