00001 #include "load_tracker.h"
00002 #include "powerflow.h"
00003
00004 CLASS* load_tracker::oclass = NULL;
00005
00006 load_tracker::load_tracker(MODULE *mod)
00007 {
00008
00009 if (oclass==NULL)
00010 {
00011
00012 oclass = gl_register_class(mod,"load_tracker",sizeof(load_tracker),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_UNSAFE_OVERRIDE_OMIT|PC_AUTOLOCK);
00013 if (oclass==NULL)
00014 GL_THROW("unable to register object class implemented by %s",__FILE__);
00015 else
00016 oclass->trl = TRL_PROVEN;
00017
00018
00019 if (gl_publish_variable(oclass,
00020 PT_object, "target",PADDR(target),PT_DESCRIPTION,"target object to track the load of",
00021 PT_char256, "target_property", PADDR(target_prop),PT_DESCRIPTION,"property on the target object representing the load",
00022 PT_enumeration, "operation", PADDR(operation),PT_DESCRIPTION,"operation to perform on complex property types",
00023 PT_KEYWORD,"REAL",(enumeration)REAL,
00024 PT_KEYWORD,"IMAGINARY",(enumeration)IMAGINARY,
00025 PT_KEYWORD,"MAGNITUDE",(enumeration)MAGNITUDE,
00026 PT_KEYWORD,"ANGLE",(enumeration)ANGLE,
00027 PT_double, "full_scale", PADDR(full_scale),PT_DESCRIPTION,"magnitude of the load at full load, used for feed-forward control",
00028 PT_double, "setpoint",PADDR(setpoint),PT_DESCRIPTION,"load setpoint to track to",
00029 PT_double, "deadband",PADDR(deadband),PT_DESCRIPTION,"percentage deadband",
00030 PT_double, "damping",PADDR(damping),PT_DESCRIPTION,"load setpoint to track to",
00031 PT_double, "output", PADDR(output),PT_DESCRIPTION,"output scaling value",
00032 PT_double, "feedback", PADDR(feedback),PT_DESCRIPTION,"the feedback signal, for reference purposes",
00033 NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00034 }
00035 }
00036
00037 int load_tracker::isa(char *classname)
00038 {
00039 return strcmp(classname,"load_tracker")==0;
00040 }
00041
00042 int load_tracker::create()
00043 {
00044 operation = MAGNITUDE;
00045 deadband = 1.0;
00046 damping = 0.0;
00047 return 1;
00048 }
00049
00050 int load_tracker::init(OBJECT *parent)
00051 {
00052
00053 if (target==NULL)
00054 {
00055 GL_THROW("Target object not set");
00056
00057
00058
00059 }
00060
00061
00062 PROPERTY* target_property = gl_get_property(target,target_prop.get_string());
00063 if (target_property==NULL)
00064 {
00065 GL_THROW("Unable to find property \"%s\" in object %s", target_prop.get_string(), target->name);
00066
00067
00068
00069 }
00070
00071
00072 switch (target_property->ptype)
00073 {
00074 case PT_double:
00075 case PT_complex:
00076 case PT_int16:
00077 case PT_int32:
00078 case PT_int64:
00079 break;
00080 default:
00081 GL_THROW("Unsupported property type. Supported types are complex, double and integer");
00082
00083
00084
00085 }
00086 type = target_property->ptype;
00087
00088
00089 switch (type)
00090 {
00091 case PT_double:
00092 pointer.d = gl_get_double_by_name(target, target_prop.get_string());
00093 break;
00094 case PT_complex:
00095 pointer.c = gl_get_complex_by_name(target, target_prop.get_string());
00096 break;
00097 case PT_int16:
00098 pointer.i16 = gl_get_int16_by_name(target, target_prop.get_string());
00099 break;
00100 case PT_int32:
00101 pointer.i32 = gl_get_int32_by_name(target, target_prop.get_string());
00102 break;
00103 case PT_int64:
00104 pointer.i64 = gl_get_int64_by_name(target, target_prop.get_string());
00105 break;
00106 }
00107
00108
00109 if (pointer.d == NULL)
00110 {
00111 GL_THROW("Unable to bind to property \"%s\" in object %s", target_prop.get_string(), target->name);
00112
00113
00114
00115 }
00116
00117
00118 if (full_scale == 0.0)
00119 {
00120 GL_THROW("The full_scale property must be non-zero");
00121
00122
00123
00124 }
00125
00126
00127 if (deadband < 1.0 || deadband > 50.0)
00128 {
00129 GL_THROW("Deadband must be in the range 1% to 50%");
00130
00131
00132
00133 }
00134
00135
00136 if (damping < 0.0)
00137 {
00138 GL_THROW("Damping must greater than or equal to 0.0");
00139
00140
00141
00142 }
00143
00144
00145 output = setpoint / full_scale;
00146
00147 return 1;
00148 }
00149
00150 void load_tracker::update_feedback_variable()
00151 {
00152
00153 READLOCK_OBJECT(target);
00154 switch (type)
00155 {
00156 case PT_double:
00157 feedback = *(pointer.d);
00158 break;
00159 case PT_complex:
00160 {
00161 switch (operation)
00162 {
00163 case REAL:
00164 feedback = pointer.c->Re();
00165 break;
00166 case IMAGINARY:
00167 feedback = pointer.c->Im();
00168 break;
00169 case MAGNITUDE:
00170 {
00171 feedback = pointer.c->Mag();
00172 if (pointer.c->Re() < 0.0)
00173 {
00174 feedback *= -1.0;
00175 }
00176 }
00177 break;
00178 case ANGLE:
00179 feedback = pointer.c->Arg();
00180 break;
00181 }
00182 }
00183 break;
00184 case PT_int16:
00185 feedback = (double)(*(pointer.i16));
00186 break;
00187 case PT_int32:
00188 feedback = (double)(*(pointer.i32));
00189 break;
00190 case PT_int64:
00191 feedback = (double)(*(pointer.i64));
00192 break;
00193 }
00194
00195 READUNLOCK_OBJECT(target);
00196 }
00197
00198 TIMESTAMP load_tracker::presync(TIMESTAMP t0)
00199 {
00200
00201
00202
00203 update_feedback_variable();
00204
00205 if (setpoint == 0.0)
00206 {
00207
00208 output = 0.0;
00209 }
00210 else if (feedback == 0.0)
00211 {
00212
00213 output = setpoint / full_scale;
00214 }
00215 else
00216 {
00217
00218
00219 if (feedback < setpoint)
00220 {
00221 double percent_error = (setpoint-feedback)/full_scale;
00222 if (percent_error > (deadband/100.0))
00223 {
00224 output *= 1.0 + ((setpoint-feedback)/feedback) * (1.0/(1.0+damping));
00225 }
00226 }
00227 else
00228 {
00229 double percent_error = (feedback-setpoint)/full_scale;
00230 if (percent_error > (deadband/100.0))
00231 {
00232 output *= 1.0 - ((feedback-setpoint)/feedback) * (1.0/(1.0+damping));
00233 }
00234 }
00235 }
00236
00237
00238 return TS_NEVER;
00239 }
00240
00241 TIMESTAMP load_tracker::sync(TIMESTAMP t0)
00242 {
00243
00244 return TS_NEVER;
00245 }
00246
00247 TIMESTAMP load_tracker::postsync(TIMESTAMP t0, TIMESTAMP t1)
00248 {
00249
00250
00251
00252 if ((solver_method == SM_FBS) || (solver_method == SM_NR && NR_admit_change == false))
00253 {
00254 update_feedback_variable();
00255
00256 if (feedback < setpoint)
00257 {
00258 double percent_error = (setpoint-feedback)/full_scale;
00259 if (percent_error > (deadband/100.0))
00260 {
00261
00262 return t1;
00263 }
00264 }
00265 else
00266 {
00267 double percent_error = (feedback-setpoint)/full_scale;
00268 if (percent_error > (deadband/100.0))
00269 {
00270
00271 return t1;
00272 }
00273 }
00274 }
00275
00276
00277 return TS_NEVER;
00278 }
00279
00280 EXPORT int isa_load_tracker(OBJECT *obj, char *classname)
00281 {
00282 return OBJECTDATA(obj,load_tracker)->isa(classname);
00283 }
00284
00285 EXPORT int create_load_tracker(OBJECT **obj, OBJECT *parent)
00286 {
00287 try
00288 {
00289 *obj = gl_create_object(load_tracker::oclass);
00290 if (*obj!=NULL)
00291 {
00292 load_tracker *my = OBJECTDATA(*obj,load_tracker);
00293 gl_set_parent(*obj,parent);
00294 return my->create();
00295 }
00296 else
00297 return 0;
00298 }
00299 CREATE_CATCHALL(load_tracker);
00300 return 0;
00301 }
00302
00303 EXPORT int init_load_tracker(OBJECT *obj)
00304 {
00305 try {
00306 load_tracker *my = OBJECTDATA(obj,load_tracker);
00307 return my->init(obj->parent);
00308 }
00309 INIT_CATCHALL(load_tracker);
00310 }
00311
00312 EXPORT TIMESTAMP sync_load_tracker(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00313 {
00314 load_tracker *pObj = OBJECTDATA(obj,load_tracker);
00315 try {
00316 TIMESTAMP t1;
00317 switch (pass) {
00318 case PC_PRETOPDOWN:
00319 return pObj->presync(t0);
00320 case PC_BOTTOMUP:
00321 return pObj->sync(t0);
00322 case PC_POSTTOPDOWN:
00323 t1 = pObj->postsync(obj->clock,t0);
00324 obj->clock = t0;
00325 return t1;
00326 default:
00327 throw "invalid pass request";
00328 }
00329 throw "invalid pass request";
00330 }
00331 SYNC_CATCHALL(load_tracker);
00332 }