mirror of
https://github.com/eddyem/small_tel.git
synced 2026-05-07 13:27:06 +03:00
add mean wind directions and max wind speed
This commit is contained in:
@@ -83,7 +83,7 @@ static glob_pars G;
|
|||||||
|
|
||||||
sl_option_t cmdlnopts[] = {
|
sl_option_t cmdlnopts[] = {
|
||||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||||
{"conffile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.conffile), "configuration file name (consists all or a part of long-named parameters and their values (e.g. plugin=liboldweather.so:D:/dev/ttyS0:115200)"},
|
{"conffile",NEED_ARG, NULL, 'c', arg_string, APTR(&G.conffile), "configuration file name (or non-existant file for help)"},
|
||||||
{"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "logfile verbocity level (each -v increased)"},
|
{"verb", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), "logfile verbocity level (each -v increased)"},
|
||||||
COMMON_OPTS
|
COMMON_OPTS
|
||||||
end_option
|
end_option
|
||||||
@@ -181,7 +181,10 @@ glob_pars *parse_args(int argc, char **argv){
|
|||||||
if(G.conffile){ // read conffile and fix parameters (cmdline args are in advantage)
|
if(G.conffile){ // read conffile and fix parameters (cmdline args are in advantage)
|
||||||
glob_pars oldpars = G; // save cmdline opts
|
glob_pars oldpars = G; // save cmdline opts
|
||||||
G = defconf;
|
G = defconf;
|
||||||
if(!sl_conf_readopts(oldpars.conffile, confopts)) ERRX("Can't get options from %s", G.conffile);
|
if(!sl_conf_readopts(oldpars.conffile, confopts)){
|
||||||
|
sl_conf_showhelp(-1, confopts);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
DBG("CONF: \n-------------\n%s-------------\n\n", sl_print_opts(confopts, 1));
|
DBG("CONF: \n-------------\n%s-------------\n\n", sl_print_opts(confopts, 1));
|
||||||
if((0 == strcmp(oldpars.port, DEFAULT_PORT)) && G.port) oldpars.port = G.port;
|
if((0 == strcmp(oldpars.port, DEFAULT_PORT)) && G.port) oldpars.port = G.port;
|
||||||
if(!oldpars.logfile && G.logfile) oldpars.logfile = G.logfile;
|
if(!oldpars.logfile && G.logfile) oldpars.logfile = G.logfile;
|
||||||
|
|||||||
@@ -24,13 +24,24 @@
|
|||||||
#include "sensors.h"
|
#include "sensors.h"
|
||||||
#include "weathlib.h"
|
#include "weathlib.h"
|
||||||
|
|
||||||
|
// wind speed history array size (not less than for one hour)
|
||||||
|
#define MAX_HISTORY 3600
|
||||||
|
// throw out data older than 24 hours
|
||||||
|
#define TOO_OLD_DATA 86400
|
||||||
|
// one hour
|
||||||
|
#define T_ONE_HOUR 3600
|
||||||
|
|
||||||
static pthread_mutex_t datamutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t datamutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static int Forbidden = 0;
|
static int Forbidden = 0;
|
||||||
|
|
||||||
// index of meteodata in array
|
// index of meteodata in array
|
||||||
enum{
|
enum{
|
||||||
NWIND,
|
NWIND,
|
||||||
|
NWINDMAX,
|
||||||
|
NWINDMAX1,
|
||||||
NWINDDIR,
|
NWINDDIR,
|
||||||
|
NWINDDIR1,
|
||||||
|
NWINDDIR2,
|
||||||
NHUMIDITY,
|
NHUMIDITY,
|
||||||
NABM_TEMP,
|
NABM_TEMP,
|
||||||
NPRESSURE,
|
NPRESSURE,
|
||||||
@@ -46,7 +57,11 @@ enum{
|
|||||||
|
|
||||||
static val_t collected_data[NAMOUNT_OF_DATA] = {
|
static val_t collected_data[NAMOUNT_OF_DATA] = {
|
||||||
[NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
|
[NWIND] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WIND},
|
||||||
|
[NWINDMAX] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDMAX", .comment = "Maximal wind speed for last 24 hours"},
|
||||||
|
[NWINDMAX1] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDMAX1", .comment = "Maximal wind speed for last hour"},
|
||||||
[NWINDDIR] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WINDDIR},
|
[NWINDDIR] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_WINDDIR},
|
||||||
|
[NWINDDIR1] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDDIR1", .comment = "Mean wind speed direction for last hour"},
|
||||||
|
[NWINDDIR2] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER, .name = "WINDDIR2", .comment = "Mean wind speed^2 direction for last hour"},
|
||||||
[NHUMIDITY] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
|
[NHUMIDITY] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_HUMIDITY},
|
||||||
[NABM_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
|
[NABM_TEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_AMB_TEMP},
|
||||||
[NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
|
[NPRESSURE] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_PRESSURE},
|
||||||
@@ -55,11 +70,101 @@ static val_t collected_data[NAMOUNT_OF_DATA] = {
|
|||||||
[NMIST] = {.sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_MIST},
|
[NMIST] = {.sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_MIST},
|
||||||
[NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS},
|
[NCLOUDS] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_CLOUDS},
|
||||||
[NSKYTEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_SKYTEMP},
|
[NSKYTEMP] = {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_SKYTEMP},
|
||||||
[NCOMMWEATH] = {.value.i = 0, .sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_OTHER, .name = "weather", .comment = "Weather level (0 - good, 3 - obs. prohibited)"},
|
[NCOMMWEATH] = {.value.i = 0, .sense = VAL_OBLIGATORY, .type = VALT_INT, .meaning = IS_OTHER, .name = "WEATHER", .comment = "Weather level (0 - good, 3 - obs. prohibited)"},
|
||||||
[NLASTAHTUNG] = {.value.i = 0, .sense = VAL_RECOMMENDED, .type = VALT_INT, .meaning = IS_OTHER, .name = "evttime", .comment = "UNIX-time of last weather level increasing"},
|
[NLASTAHTUNG] = {.value.i = 0, .sense = VAL_RECOMMENDED, .type = VALT_INT, .meaning = IS_OTHER, .name = "EVTTIME", .comment = "UNIX-time of last weather level increasing"},
|
||||||
// {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER},
|
// {.sense = VAL_OBLIGATORY, .type = VALT_FLOAT, .meaning = IS_OTHER},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
double array[MAX_HISTORY];
|
||||||
|
double sum;
|
||||||
|
} sumval_t;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int write_idx;
|
||||||
|
sumval_t C; // (speed * cos(dir))
|
||||||
|
sumval_t C2; // (speed^2 * cos(dir))
|
||||||
|
sumval_t S; // (speed * sin(dir))
|
||||||
|
sumval_t S2; // (speed^2 * sin(dir))
|
||||||
|
} meanwinddir_t;
|
||||||
|
|
||||||
|
static meanwinddir_t winddirs = {0};
|
||||||
|
|
||||||
|
static double update_sum(sumval_t *sv, double newval, int oldidx){
|
||||||
|
double old = sv->array[oldidx];
|
||||||
|
sv->array[oldidx] = newval;
|
||||||
|
return (sv->sum += newval - old);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add current value into floating array and recalculate mean speeds
|
||||||
|
// data mutex should be locked outside this function
|
||||||
|
static void wind_dir_add(double curspeed, double curdir, double *dir, double *dir2){
|
||||||
|
double C, S;
|
||||||
|
sincos(curdir * M_PI / 180., &S, &C);
|
||||||
|
double vS = curspeed * S, vC = curspeed * C, v2S = curspeed * vS, v2C = curspeed * vC;
|
||||||
|
int idx = winddirs.write_idx;
|
||||||
|
winddirs.write_idx = (idx + 1) % MAX_HISTORY;
|
||||||
|
// calculate sums
|
||||||
|
vS = update_sum(&winddirs.S, vS, idx);
|
||||||
|
vC = update_sum(&winddirs.C, vC, idx);
|
||||||
|
v2S = update_sum(&winddirs.S2, v2S, idx);
|
||||||
|
v2C = update_sum(&winddirs.C2, v2C, idx);
|
||||||
|
*dir = atan2(vS, vC) * 180. / M_PI;
|
||||||
|
*dir2 = atan2(v2S, v2C) * 180. / M_PI;
|
||||||
|
if(*dir < 0.) *dir += 360.;
|
||||||
|
if(*dir2 < 0.) *dir2 += 360.;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
double speeds[MAX_HISTORY];
|
||||||
|
time_t timestamps[MAX_HISTORY];
|
||||||
|
int write_idx; // index in `speeds` and `timestamps` for new value
|
||||||
|
|
||||||
|
int deq[MAX_HISTORY]; // array of indexes in queue
|
||||||
|
int deq_head, deq_tail; // queue's head and tail
|
||||||
|
} sliding_max_t;
|
||||||
|
|
||||||
|
static sliding_max_t windspeeds = {0};
|
||||||
|
|
||||||
|
static void add_windspeed(sliding_max_t *sm, double speed, time_t now) {
|
||||||
|
// Write new data portion into queue
|
||||||
|
int idx = sm->write_idx;
|
||||||
|
sm->speeds[idx] = speed;
|
||||||
|
sm->timestamps[idx] = now;
|
||||||
|
sm->write_idx = (idx + 1) % MAX_HISTORY;
|
||||||
|
|
||||||
|
// Remove values older than `TOO_OLD_DATA`
|
||||||
|
time_t cutoff = now - TOO_OLD_DATA;
|
||||||
|
while(sm->deq_head != sm->deq_tail && sm->timestamps[sm->deq[sm->deq_head]] < cutoff){
|
||||||
|
sm->deq_head = (sm->deq_head + 1) % MAX_HISTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove small values less than current
|
||||||
|
while(sm->deq_head != sm->deq_tail && sm->speeds[sm->deq[(sm->deq_tail - 1 + MAX_HISTORY) % MAX_HISTORY]] <= speed){
|
||||||
|
sm->deq_tail = (sm->deq_tail - 1 + MAX_HISTORY) % MAX_HISTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new index into queue
|
||||||
|
sm->deq[sm->deq_tail] = idx;
|
||||||
|
sm->deq_tail = (sm->deq_tail + 1) % MAX_HISTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double get_current_max(sliding_max_t *sm){
|
||||||
|
if(sm->deq_head == sm->deq_tail) return 0.0; // No data
|
||||||
|
return sm->speeds[sm->deq[sm->deq_head]];
|
||||||
|
}
|
||||||
|
|
||||||
|
static double get_max_forT(sliding_max_t *sm, time_t tcutoff){
|
||||||
|
if(sm->deq_head == sm->deq_tail) return 0.0; // No data
|
||||||
|
int idx = sm->deq_head;
|
||||||
|
while(idx != sm->deq_tail && sm->timestamps[sm->deq[sm->deq_head]] < tcutoff){
|
||||||
|
idx = (idx + 1) % MAX_HISTORY;
|
||||||
|
}
|
||||||
|
if(idx == sm->deq_tail) return 0.0; // No fresh data
|
||||||
|
return sm->speeds[sm->deq[idx]];
|
||||||
|
}
|
||||||
|
|
||||||
int collected_amount(){
|
int collected_amount(){
|
||||||
return NAMOUNT_OF_DATA;
|
return NAMOUNT_OF_DATA;
|
||||||
}
|
}
|
||||||
@@ -137,7 +242,7 @@ static void chkweatherlevel(int *curlevel, double curvalue, weather_cond_t *curc
|
|||||||
DBG("newlevel: %d, current: %d INCREASED", newlevel, *curlevel);
|
DBG("newlevel: %d, current: %d INCREASED", newlevel, *curlevel);
|
||||||
*curlevel = newlevel;
|
*curlevel = newlevel;
|
||||||
if(*ahtungtime < curt) *ahtungtime = (int) curt; // refresh event time
|
if(*ahtungtime < curt) *ahtungtime = (int) curt; // refresh event time
|
||||||
}else{ // check timeout to make level lower
|
}else if(newlevel < *curlevel){ // check timeout to make level lower
|
||||||
if(curt - *ahtungtime > WeatherConf.ahtung_delay){
|
if(curt - *ahtungtime > WeatherConf.ahtung_delay){
|
||||||
DBG("newlevel: %d, current: %d DECREASED", newlevel, *curlevel);
|
DBG("newlevel: %d, current: %d DECREASED", newlevel, *curlevel);
|
||||||
*curlevel = newlevel;
|
*curlevel = newlevel;
|
||||||
@@ -154,6 +259,7 @@ void refresh_sensval(sensordata_t *s){
|
|||||||
int curlevel = collected_data[NCOMMWEATH].value.i;
|
int curlevel = collected_data[NCOMMWEATH].value.i;
|
||||||
int curahtungtime = collected_data[NLASTAHTUNG].value.i;
|
int curahtungtime = collected_data[NLASTAHTUNG].value.i;
|
||||||
time_t curtime = time(NULL);
|
time_t curtime = time(NULL);
|
||||||
|
double dir = -100., dir2 = -100.; // mean wind directions
|
||||||
DBG("%d meteo values", s->Nvalues);
|
DBG("%d meteo values", s->Nvalues);
|
||||||
for(int i = 0; i < s->Nvalues; ++i){
|
for(int i = 0; i < s->Nvalues; ++i){
|
||||||
DBG("Try to get %dth value", i);
|
DBG("Try to get %dth value", i);
|
||||||
@@ -167,9 +273,11 @@ void refresh_sensval(sensordata_t *s){
|
|||||||
idx = NWIND;
|
idx = NWIND;
|
||||||
curvalue = (double) value.value.f;
|
curvalue = (double) value.value.f;
|
||||||
curcond = &WeatherConf.wind;
|
curcond = &WeatherConf.wind;
|
||||||
|
add_windspeed(&windspeeds, curvalue, curtime);
|
||||||
break;
|
break;
|
||||||
case IS_WINDDIR:
|
case IS_WINDDIR:
|
||||||
idx = NWINDDIR;
|
idx = NWINDDIR;
|
||||||
|
wind_dir_add(collected_data[NWIND].value.f, value.value.f, &dir, &dir2);
|
||||||
break;
|
break;
|
||||||
case IS_HUMIDITY:
|
case IS_HUMIDITY:
|
||||||
idx = NHUMIDITY;
|
idx = NHUMIDITY;
|
||||||
@@ -224,8 +332,21 @@ void refresh_sensval(sensordata_t *s){
|
|||||||
pthread_mutex_unlock(&datamutex);
|
pthread_mutex_unlock(&datamutex);
|
||||||
if(!Forbidden && curcond) chkweatherlevel(&curlevel, curvalue, curcond, &curahtungtime);
|
if(!Forbidden && curcond) chkweatherlevel(&curlevel, curvalue, curcond, &curahtungtime);
|
||||||
}
|
}
|
||||||
DBG("check ahtung");
|
|
||||||
pthread_mutex_lock(&datamutex);
|
pthread_mutex_lock(&datamutex);
|
||||||
|
// refresh max
|
||||||
|
if(dir >= 0.){
|
||||||
|
collected_data[NWINDDIR1].value.f = (float) dir;
|
||||||
|
collected_data[NWINDDIR1].time = curtime;
|
||||||
|
}
|
||||||
|
if(dir2 >= 0.){
|
||||||
|
collected_data[NWINDDIR2].value.f = (float) dir2;
|
||||||
|
collected_data[NWINDDIR2].time = curtime;
|
||||||
|
}
|
||||||
|
collected_data[NWINDMAX].value.f = (float) get_current_max(&windspeeds);
|
||||||
|
collected_data[NWINDMAX].time = curtime;
|
||||||
|
collected_data[NWINDMAX1].value.f = (float) get_max_forT(&windspeeds, curtime - T_ONE_HOUR);
|
||||||
|
collected_data[NWINDMAX1].time = curtime;
|
||||||
|
DBG("check ahtung");
|
||||||
if(Forbidden) collected_data[NCOMMWEATH].value.i = WEATHER_PROHIBITED;
|
if(Forbidden) collected_data[NCOMMWEATH].value.i = WEATHER_PROHIBITED;
|
||||||
else collected_data[NCOMMWEATH].value.i = curlevel;
|
else collected_data[NCOMMWEATH].value.i = curlevel;
|
||||||
if(collected_data[NLASTAHTUNG].value.i < curahtungtime) collected_data[NLASTAHTUNG].value.i = curahtungtime;
|
if(collected_data[NLASTAHTUNG].value.i < curahtungtime) collected_data[NLASTAHTUNG].value.i = curahtungtime;
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
|
|||||||
switch(v->type){
|
switch(v->type){
|
||||||
case VALT_UINT: snprintf(strval, VAL_LEN, "%u", v->value.u); break;
|
case VALT_UINT: snprintf(strval, VAL_LEN, "%u", v->value.u); break;
|
||||||
case VALT_INT: snprintf(strval, VAL_LEN, "%d", v->value.i); break;
|
case VALT_INT: snprintf(strval, VAL_LEN, "%d", v->value.i); break;
|
||||||
case VALT_FLOAT: snprintf(strval, VAL_LEN, "%g", v->value.f); break;
|
case VALT_FLOAT: snprintf(strval, VAL_LEN, "%.2f", v->value.f); break;
|
||||||
default: sprintf(strval, "'ERROR'");
|
default: sprintf(strval, "'ERROR'");
|
||||||
}
|
}
|
||||||
const char* const NM[IS_OTHER] = { // names of standard fields
|
const char* const NM[IS_OTHER] = { // names of standard fields
|
||||||
@@ -196,7 +196,7 @@ int format_sensval(const val_t *v, char *buf, int buflen, int Np){
|
|||||||
};
|
};
|
||||||
const char* const CMT[IS_OTHER] = { // comments for standard fields
|
const char* const CMT[IS_OTHER] = { // comments for standard fields
|
||||||
[IS_WIND] = "Wind, m/s",
|
[IS_WIND] = "Wind, m/s",
|
||||||
[IS_WINDDIR] = "Wind direction, degr (CW from north to FROM)",
|
[IS_WINDDIR] = "Instant wind direction, degr (CW from north to FROM)",
|
||||||
[IS_HUMIDITY] = "Humidity, percent",
|
[IS_HUMIDITY] = "Humidity, percent",
|
||||||
[IS_AMB_TEMP] = "Ambient temperature, degC",
|
[IS_AMB_TEMP] = "Ambient temperature, degC",
|
||||||
[IS_INNER_TEMP] = "In-dome temperature, degC",
|
[IS_INNER_TEMP] = "In-dome temperature, degC",
|
||||||
|
|||||||
@@ -101,9 +101,9 @@ static void showdata(sl_sock_t *client){
|
|||||||
time_t oldest = time(NULL) - oldest_interval;
|
time_t oldest = time(NULL) - oldest_interval;
|
||||||
uint64_t Tsum = 0; int nsum = 0;
|
uint64_t Tsum = 0; int nsum = 0;
|
||||||
for(int i = 0; i < Ncoll; ++i){
|
for(int i = 0; i < Ncoll; ++i){
|
||||||
if(!get_collected(&v, i)) continue;
|
if(!get_collected(&v, i)){ DBG("Can't get %dth value", i); continue; }
|
||||||
if(v.time < oldest) continue;
|
if(v.time < oldest){ DBG("%dth value is too old", i); continue; }
|
||||||
if(1 > format_sensval(&v, buf, FULL_LEN+1, -1)) continue;
|
if(1 > format_sensval(&v, buf, FULL_LEN+1, -1)){ DBG("Can't format"); continue; }
|
||||||
DBG("formatted: '%s'", buf);
|
DBG("formatted: '%s'", buf);
|
||||||
sl_sock_sendstrmessage(client, buf);
|
sl_sock_sendstrmessage(client, buf);
|
||||||
sl_sock_sendbyte(client, '\n');
|
sl_sock_sendbyte(client, '\n');
|
||||||
|
|||||||
Reference in New Issue
Block a user