Changeset 17027
- Timestamp:
- 08/19/13 16:36:59 (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/FACT++/src/feedback.cc
r16967 r17027 1 1 #include <valarray> 2 #include <algorithm> 2 3 3 4 #include "Dim.h" … … 8 9 #include "Configuration.h" 9 10 #include "Console.h" 10 #include "Converter.h"11 11 #include "externals/PixelMap.h" 12 12 … … 15 15 #include "LocalControl.h" 16 16 17 #include "HeadersFAD.h"18 17 #include "HeadersFSC.h" 19 18 #include "HeadersBIAS.h" … … 30 29 { 31 30 private: 32 enum control_t33 {34 kIdle,35 kTemp,36 kFeedback,37 kFeedbackGlobal,38 kCurrents,39 kCurrentsNew,40 };41 42 control_t fControlType;43 44 31 PixelMap fMap; 45 32 33 bool fIsVerbose; 34 bool fEnableOldAlgorithm; 35 46 36 DimVersion fDim; 47 DimDescribedState fDimFAD; 37 48 38 DimDescribedState fDimFSC; 49 39 DimDescribedState fDimBias; 50 40 51 DimDescribedService fDimReference;52 DimDescribedService fDimDeviation;53 41 DimDescribedService fDimCalibration; 42 DimDescribedService fDimCalibration2; 43 DimDescribedService fDimCalibrationR8; 54 44 DimDescribedService fDimCurrents; 45 46 vector<float> fCalibCurrentMes[6]; // Measured calibration current at six different levels 47 vector<float> fCalibVoltage[6]; // Corresponding voltage as reported by biasctrl 55 48 56 49 vector<int64_t> fCurrentsAvg; 57 50 vector<int64_t> fCurrentsRms; 58 51 52 vector<float> fVoltGapd; // Nominal breakdown voltage + 1.1V 53 vector<float> fBiasVolt; // Output voltage as reported by bias crate (voltage between R10 and R8) 54 vector<uint16_t> fBiasDac; // Dac value corresponding to the voltage setting 55 59 56 vector<float> fCalibration; 60 vector<float> fVoltGapd; 61 vector<float> fBiasVolt; 62 63 vector<vector<float>> fData; 57 vector<float> fCalibDeltaI; 58 vector<float> fCalibR8; 64 59 65 60 int64_t fCursorCur; 66 uint64_t fCursorAmpl; 67 uint64_t fCursorTemp; 68 69 Time fBiasLast; 70 Time fStartTime; 71 Time fCalibTime; 72 73 valarray<double> fPV[3]; // Process variable (intgerated/averaged amplitudes) 74 valarray<double> fSP; // Set point (target amplitudes) 75 76 double fKp; // Proportional constant 77 double fKi; // Integral constant 78 double fKd; // Derivative constant 79 double fT; // Time constant (cycle time) 80 double fGain; // Gain (conversion from a DRS voltage deviation into a BIAS voltage change at G-APD reference voltage) 81 82 double fT21; 83 84 double fBiasOffset; 61 62 Time fTimeCalib; 63 Time fTimeTemp; 64 65 double fUserOffset; 85 66 double fTempOffset; 86 double fCalibrationOffset; 87 double fAppliedOffset; 67 double fTemp; 88 68 89 69 uint16_t fCurrentRequestInterval; 90 70 uint16_t fNumCalibIgnore; 91 71 uint16_t fNumCalibRequests; 92 93 bool fOutputEnabled; 72 uint16_t fCalibStep; 73 74 // ============================= Handle Services ======================== 75 76 int HandleBiasStateChange() 77 { 78 if (fDimBias.state()==BIAS::State::kVoltageOn && GetCurrentState()==Feedback::State::kCalibrating) 79 { 80 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS"); 81 Info("Starting calibration step "+to_string(fCalibStep)); 82 } 83 84 if (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()==Feedback::State::kInProgress) 85 return Feedback::State::kCalibrated; 86 87 return GetCurrentState(); 88 } 89 // ============================= Handle Services ======================== 90 91 bool CheckEventSize(size_t has, const char *name, size_t size) 92 { 93 if (has==size) 94 return true; 95 96 // Disconnected 97 if (has==0) 98 return false; 99 100 ostringstream msg; 101 msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; 102 Fatal(msg); 103 return false; 104 } 105 106 int HandleBiasNom(const EventImp &evt) 107 { 108 if (evt.GetSize()>=416*sizeof(float)) 109 { 110 fVoltGapd.assign(evt.Ptr<float>(), evt.Ptr<float>()+416); 111 Info("Nominal bias voltages and calibration resistor received."); 112 } 113 114 return GetCurrentState(); 115 } 116 117 int HandleBiasVoltage(const EventImp &evt) 118 { 119 if (evt.GetSize()>=416*sizeof(float)) 120 fBiasVolt.assign(evt.Ptr<float>(), evt.Ptr<float>()+416); 121 return GetCurrentState(); 122 } 123 124 int HandleBiasDac(const EventImp &evt) 125 { 126 if (evt.GetSize()>=416*sizeof(uint16_t)) 127 fBiasDac.assign(evt.Ptr<uint16_t>(), evt.Ptr<uint16_t>()+416); 128 return GetCurrentState(); 129 } 94 130 95 131 int HandleCameraTemp(const EventImp &evt) 96 132 { 97 if ( fControlType!=kTemp && fControlType!=kCurrents && fControlType!=kCurrentsNew)98 return GetCurrentState();99 100 if (evt.GetSize()!=60*sizeof(float))101 return GetCurrentState();133 if (!CheckEventSize(evt.GetSize(), "HandleCameraTemp", 60*sizeof(float))) 134 { 135 fTimeTemp = Time(Time::none); 136 return GetCurrentState(); 137 } 102 138 103 139 const float *ptr = evt.Ptr<float>(); … … 120 156 avgt /= numt; // [deg C] 121 157 122 fTempOffset = (avgt-25)*4./70; // [V] 123 124 fCursorTemp++; 125 126 return fControlType==kCurrentsNew ? HandleCurrentControlNew() : HandleCurrentControl(); 127 } 128 129 int HandleCurrentControlNew() 130 { 131 if (GetCurrentState()==Feedback::State::kCalibrating && fBiasOffset>fTempOffset-1.2) 132 { 133 fCursorTemp = 0; 134 135 ostringstream msg; 136 msg << " (applied calibration offset " << fBiasOffset << "V exceeds temperature correction " << fTempOffset << "V - 1.2V."; 137 Warn("Trying to calibrate above G-APD breakdown volatge!"); 138 Warn(msg); 139 return GetCurrentState(); 140 } 158 fTimeTemp = evt.GetTime(); 159 fTempOffset = (avgt-25)*0.0561765; // [V] From Hamamatsu datasheet 160 fTemp = avgt; 161 162 return GetCurrentState(); 163 } 164 165 pair<vector<float>, vector<float>> AverageCurrents(const int16_t *ptr, int n) 166 { 167 if (fCursorCur++>=0) 168 { 169 for (int i=0; i<BIAS::kNumChannels; i++) 170 { 171 fCurrentsAvg[i] += ptr[i]; 172 fCurrentsRms[i] += ptr[i]*ptr[i]; 173 } 174 } 175 176 if (fCursorCur<n) 177 return make_pair(vector<float>(), vector<float>()); 178 179 const double conv = 5e-3/4096; 180 181 vector<float> rms(BIAS::kNumChannels); 182 vector<float> avg(BIAS::kNumChannels); 183 for (int i=0; i<BIAS::kNumChannels; i++) 184 { 185 avg[i] = double(fCurrentsAvg[i])/fCursorCur * conv; 186 rms[i] = double(fCurrentsRms[i])/fCursorCur * conv * conv; 187 188 rms[i] = sqrt(rms[i]-avg[i]*avg[i]); 189 } 190 191 return make_pair(avg, rms); 192 } 193 194 int HandleCalibration(const EventImp &evt) 195 { 196 const uint16_t dac = 256+512*fCalibStep; // Command value 197 198 // Only the channels which are no spare channels are ramped 199 if (std::count(fBiasDac.begin(), fBiasDac.end(), dac)!=320) 200 return GetCurrentState(); 201 202 const auto rc = AverageCurrents(evt.Ptr<int16_t>(), fNumCalibRequests); 203 if (rc.first.size()==0) 204 { 205 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS"); 206 return GetCurrentState(); 207 } 208 209 const vector<float> &avg = rc.first; 210 const vector<float> &rms = rc.second; 211 212 // Current through resistor R8 213 fCalibCurrentMes[fCalibStep] = avg; // [A] 214 fCalibVoltage[fCalibStep] = fBiasVolt; // [V] 215 216 // ------------------------- Update calibration data -------------------- 217 218 struct cal_data 219 { 220 uint32_t dac; 221 float U[416]; 222 float Iavg[416]; 223 float Irms[416]; 224 225 cal_data() { memset(this, 0, sizeof(cal_data)); } 226 } __attribute__((__packed__)); 227 228 cal_data cal; 229 cal.dac = dac; 230 memcpy(cal.U, fBiasVolt.data(), 416*sizeof(float)); 231 memcpy(cal.Iavg, avg.data(), 416*sizeof(float)); 232 memcpy(cal.Irms, rms.data(), 416*sizeof(float)); 233 234 fDimCalibration2.setData(fCalibration); 235 fDimCalibration2.Update(fTimeCalib); 236 237 // -------------------- Start next calibration steo --------------------- 238 239 if (++fCalibStep<6) 240 { 241 fCursorCur = -fNumCalibIgnore; 242 fCurrentsAvg.assign(BIAS::kNumChannels, 0); 243 fCurrentsRms.assign(BIAS::kNumChannels, 0); 244 245 Dim::SendCommandNB("BIAS_CONTROL/SET_GLOBAL_DAC", uint32_t(256+512*fCalibStep)); 246 247 return GetCurrentState(); 248 } 249 250 // --------------- Calculate old style calibration ---------------------- 251 252 fCalibration.resize(BIAS::kNumChannels*4); 253 254 float *pavg = fCalibration.data(); 255 float *prms = fCalibration.data()+BIAS::kNumChannels; 256 float *pres = fCalibration.data()+BIAS::kNumChannels*2; 257 float *pUmes = fCalibration.data()+BIAS::kNumChannels*3; 258 259 for (int i=0; i<BIAS::kNumChannels; i++) 260 { 261 const double I = fCalibCurrentMes[5][i]; // [A] 262 const double U = fBiasVolt[i]; // [V] 263 264 pavg[i] = I*1e6; // [uA] 265 prms[i] = rms[i]*1e6; // [uA] 266 pres[i] = U/I; // [Ohm] 267 pUmes[i] = U; // [V] 268 } 269 270 fDimCalibration.setData(fCalibration); 271 fDimCalibration.Update(fTimeCalib); 272 273 // -------------------- New style calibration -------------------------- 274 275 fCalibDeltaI.resize(BIAS::kNumChannels); 276 fCalibR8.resize(BIAS::kNumChannels); 277 278 // Linear regression of the values at 256+512*N for N={ 3, 4, 5 } 279 for (int i=0; i<BIAS::kNumChannels; i++) 280 { 281 // x: Idac 282 // y: Iadc 283 284 double x = 0; 285 double y = 0; 286 double xx = 0; 287 double xy = 0; 288 289 const int beg = 3; 290 const int end = 5; 291 const int len = end-beg+1; 292 293 for (int j=beg; j<=end; j++) 294 { 295 const double Idac = (256+512*j)*1e-3/4096; 296 297 x += Idac; 298 xx += Idac*Idac; 299 y += fCalibCurrentMes[j][i]; 300 xy += fCalibCurrentMes[j][i]*Idac; 301 } 302 303 const double m1 = xy - x*y / len; 304 const double m2 = xx - x*x / len; 305 306 const double m = m2==0 ? 0 : m1/m2; 307 308 const double t = (y - m*x) / len; 309 310 fCalibDeltaI[i] = t; // [A] 311 fCalibR8[i] = 100/m; // [Ohm] 312 } 313 314 vector<float> v(BIAS::kNumChannels*2); 315 v.insert(v.end(), fCalibDeltaI.begin(), fCalibDeltaI.end()); 316 v.insert(v.end(), fCalibR8.begin(), fCalibR8.end()); 317 318 fDimCalibrationR8.setData(v); 319 fDimCalibrationR8.Update(fTimeCalib); 320 321 // --------------------------------------------------------------------- 322 323 Info("Calibration successfully done."); 324 Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE"); 325 326 return Feedback::State::kCalibrated; 327 } 328 329 int HandleBiasCurrent(const EventImp &evt) 330 { 331 if (!CheckEventSize(evt.GetSize(), "HandleBiasCurrent", BIAS::kNumChannels*sizeof(uint16_t))) 332 return Feedback::State::kConnected; 333 334 if (GetCurrentState()<Feedback::State::kCalibrating) 335 return GetCurrentState(); 336 337 // FIXME? Allow for calibrated currents also during ramping? 338 if ((GetCurrentState()!=Feedback::State::kWaitingForData || fDimBias.state()!=BIAS::State::kVoltageOff) && 339 fDimBias.state()!=BIAS::State::kVoltageOn) 340 return GetCurrentState(); 341 342 // ------------------------------- HandleCalibration ----------------------------------- 343 if (GetCurrentState()==Feedback::State::kCalibrating) 344 return HandleCalibration(evt); 345 346 // ---------------------- Calibrated, WaitingForData, InProgress ----------------------- 347 348 // We are waiting but no valid temperature yet, go on waiting 349 if (GetCurrentState()==Feedback::State::kWaitingForData && 350 (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5))) 351 return GetCurrentState(); 352 353 // We are already in progress but no valid temperature update anymore 354 if (GetCurrentState()==Feedback::State::kInProgress && 355 (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5))) 356 { 357 Warn("Current control in progress, but last received temperature older than 5min... switching voltage off."); 358 Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE"); 359 return Feedback::State::kCalibrated; 360 } 361 362 // ---------------------- Calibrated, WaitingForData, InProgress ----------------------- 363 364 const vector<float> &Imes = AverageCurrents(evt.Ptr<int16_t>(), 3).first; 365 if (Imes.size()==0) 366 return GetCurrentState(); 367 368 fCurrentsAvg.assign(416, 0); 369 fCurrentsRms.assign(416, 0); 370 fCursorCur = 0; 371 372 // ------------------------------------------------------------------------------------- 373 374 // Nominal overvoltage (w.r.t. the bias setup values) 375 const double overvoltage = GetCurrentState()<Feedback::State::kWaitingForData ? 0 : fUserOffset; 141 376 142 377 double avg[2] = { 0, 0 }; 143 378 double min[2] = { 90, 90 }; 144 379 double max[2] = { -90, -90 }; 145 int num[ 2] = {0, 0 };146 147 vector<double> med[ 2];380 int num[3] = { 0, 0, 0 }; 381 382 vector<double> med[3]; 148 383 med[0].resize(416); 149 384 med[1].resize(416); 150 151 //const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance 152 153 vector<float> vec(2*BIAS::kNumChannels+2); 154 155 vec[BIAS::kNumChannels*2] = fTempOffset; 156 vec[BIAS::kNumChannels*2+1] = fBiasOffset; 157 158 float *Uoff = vec.data()+BIAS::kNumChannels; 159 160 if (GetCurrentState()==Feedback::State::kCalibrating) 161 for (int i=0; i<BIAS::kNumChannels; i++) 162 Uoff[i] = fBiasOffset; 163 else 164 for (int i=0; i<BIAS::kNumChannels; i++) 165 Uoff[i] = fTempOffset+fBiasOffset; 166 167 if (fControlType==kCurrentsNew) 168 { 169 // Would be a devision by zero. We need informations first. 170 if (fCursorCur==0) 171 return GetCurrentState(); 172 173 vector<double> dI; 174 vector<double> R8; 175 vector<double> R9; 176 177 for (int i=0; i<BIAS::kNumChannels; i++) 385 med[2].resize(416); 386 387 struct dim_data 388 { 389 float I[416]; 390 float Iavg; 391 float Irms; 392 float Imed; 393 float Idev; 394 uint32_t N; 395 float Tdiff; 396 float Uov[416]; 397 float Unom; 398 float dUtemp; 399 400 dim_data() { memset(this, 0, sizeof(dim_data)); } 401 } __attribute__((__packed__)); 402 403 dim_data data; 404 405 data.Unom = overvoltage; 406 data.dUtemp = fTempOffset; 407 408 vector<float> vec(416); 409 410 cout << setprecision(4) << endl; 411 412 if (fEnableOldAlgorithm) 413 { 414 // Pixel 583: 5 31 == 191 (5) C2 B3 P3 415 // Pixel 830: 2 2 == 66 (4) C0 B8 P1 416 // Pixel 1401: 6 1 == 193 (5) C2 B4 P0 417 418 // 3900 Ohm/n + 1000 Ohm + 1100 Ohm (with n=4 or n=5) 419 const double R[2] = { 3075, 2870 }; 420 421 const float *Iavg = fCalibration.data(); // Offset at U=fCalibrationOffset 422 const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance 423 424 for (int i=0; i<320; i++) 178 425 { 179 426 const PixelMapEntry &hv = fMap.hv(i); … … 181 428 continue; 182 429 183 // Nominal breakdown voltage (includes overvoltage already) 184 double Ubd = fVoltGapd[i]; 185 186 // Nominal breakdown voltage excluding overvoltage of 1.1V 187 Ubd -= 1.1; 188 189 // Correct breakdown voltage for temperature dependence 190 Ubd += fTempOffset; 430 // Average measured current 431 const double Im = Imes[i] * 1e6; // [uA] 432 433 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch) 434 const int g = hv.group(); 435 436 // Serial resistors in front of the G-APD 437 double Rg = R[g]; 438 439 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor 440 if (i==66) // Pixel 830(66) 441 Rg = 2400; // 2400 = (3/3900 + 1/390) + 1000 + 1100 442 if (i==191 || i==193) // Pixel 583(191) / Pixel 1401(193) 443 Rg = 2379; // 2379 = (4/3900 + 1/390) + 1000 + 1100 444 445 const double r = 1./(1./Rg + 1./Ravg[i]); // [Ohm] 446 447 // Offset induced by the voltage above the calibration point 448 const double Ubd = fVoltGapd[i] + fTempOffset; 449 const double U0 = Ubd + overvoltage - fCalibVoltage[5][i]; // appliedOffset-fCalibrationOffset; 450 const double dI = U0/Ravg[i]; // [V/Ohm] 451 452 // Offset at the calibration point (make sure that the calibration is 453 // valid (Im[i]>Iavg[i]) and we operate above the calibration point) 454 const double I = Im>Iavg[i] ? Im - Iavg[i] : 0; // [uA] 455 456 // Make sure that the averaged resistor is valid 457 const double dU = Ravg[i]>10000 ? r*(I*1e-6 - dI) : 0; 458 459 if (i==2) 460 cout << dU << endl;; 461 462 vec[i] = Ubd + overvoltage + dU; 463 464 // Calculate statistics only for channels with a valid calibration 465 if (Iavg[i]>0) 466 { 467 med[g][num[g]] = dU; 468 avg[g] += dU; 469 num[g]++; 470 471 if (dU<min[g]) 472 min[g] = dU; 473 if (dU>max[g]) 474 max[g] = dU; 475 476 data.I[i] = Imes[i]*1e6 - fBiasVolt[i]/Ravg[i]*1e6; 477 data.I[i] /= hv.count(); 478 479 if (i==66) 480 data.I[i] /= 1.3; 481 if (i==191 || i==193) 482 data.I[i] /= 1.4; 483 484 data.Iavg += data.I[i]; 485 data.Irms += data.I[i]*data.I[i]; 486 487 med[2][num[2]++] = data.I[i]; 488 } 489 } 490 } 491 else 492 { 493 /* ================================= new ======================= */ 494 495 for (int i=0; i<320/*BIAS::kNumChannels*/; i++) 496 { 497 const PixelMapEntry &hv = fMap.hv(i); 498 if (!hv) 499 continue; 191 500 192 501 // Number of G-APDs in this patch 193 const int N = hv. group() ? 5 : 4;502 const int N = hv.count(); 194 503 195 504 // Average measured ADC value for this channel 196 const double adc = double(fCurrentsAvg[i])/fCursorCur * (5000/4096.); // [uA] 197 198 // Current through ~100Ohm measurement resistor 199 const double I8 = (adc-dI[i])*100/R8[i]; 505 const double adc = Imes[i]/* * (5e-3/4096)*/; // [A] 506 507 // Current through ~100 Ohm measurement resistor 508 const double I8 = (adc-fCalibDeltaI[i])*fCalibR8[i]/100; 509 510 // Applied voltage at calibration resistors, according to biasctrl 511 const double U9 = fBiasVolt[i]; 512 513 // Current through calibration resistors (R9) 514 // This is uncalibrated, biut since the corresponding calibrated 515 // value I8 is subtracted, the difference should yield a correct value 516 const double I9 = fBiasDac[i] * (1e-3/4096);//U9/R9; [A] 517 518 // Current in R4/R5 branch 519 const double Iout = I8>I9 ? I8 - I9 : 0; 200 520 201 521 // Serial resistors (one 1kOhm at the output of the bias crate, one 1kOhm in the camera) … … 211 531 R5 = 390/1.4; // 379 = 1/(4/3900 + 1/390) 212 532 213 // Total resistance of branch with diode 533 // Total resistance of branch with diodes 214 534 const double R = R4+R5; 215 535 216 // Applied voltage at calibration resistors, according to 217 // biasctrl 218 const double U9 = fBiasVolt[i]; 219 220 // Current through calibration resistors 221 // FIXME: Get that from biasctrl!!! 222 const double I9 = U9/R9[i]; 223 224 // Current in R4/R5 branch 225 const double Iout = I8>I9 ? I8 - I9 : 0; 226 227 // Voltage drop in R4/R5 branch 536 // For the patches with a broken resistor - ignoring the G-APD resistance - 537 // we get: 538 // 539 // I[R=3900] = Iout * 1/(10+(N-1)) = Iout /(N+9) 540 // I[R= 390] = Iout * (1 - 1/ (10+(N-1))) = Iout * (N+8)/(N+9) 541 // 542 // I[R=390] / I[R=3900] = N+8 543 // 544 // Udrp = Iout*3900/(N+9) + Iout*1000 + Iout*1000 = Iout * R 545 546 // Voltage drop in R4/R5 branch (for the G-APDs with correct resistor) 228 547 const double Udrp = R*Iout; 229 548 230 // Current overvoltage 549 // Nominal breakdown voltage with correction for temperature dependence 550 const double Ubd = fVoltGapd[i] + fTempOffset; 551 552 // Current overvoltage (at a G-APD with the correct 3900 Ohm resistor) 231 553 const double Uov = U9-Udrp-Ubd>0 ? U9-Udrp-Ubd : 0; 554 555 // Iout linear with U9 above Ubd 556 // 557 // Rx = (U9-Ubd)/Iout 558 // I' = (U9'-Ubd) / Rx 559 // Udrp' = R*I' 560 // Uov = U9' - Udrp' - Ubd 561 // Uov = overvoltage 562 // 563 // overvoltage = U9' - Udrp' - Ubd 564 // overvoltage = U9' - R*I' - Ubd 565 // overvoltage = U9' - R*((U9'-Ubd)/Rx) - Ubd 566 // overvoltage = U9' - U9'*R/Rx + Ubd*R/Rx - Ubd 567 // overvoltage = U9'*(1 - R/Rx) + Ubd*R/Rx - Ubd 568 // overvoltage - Ubd*R/Rx +Ubd = U9'*(1 - R/Rx) 569 // U9' = [ overvoltage - Ubd*R/Rx +Ubd ] / (1 - R/Rx) 570 // 232 571 233 572 // The current through one G-APD is the sum divided by the number of G-APDs … … 235 574 double Iapd = Iout/N; 236 575 237 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor238 576 // In this and the previosu case we neglect the resistance of the G-APDs, but we can make an 239 577 // assumption: The differential resistance depends more on the NSB than on the PDE, … … 244 582 // increase the current flow as compared to the total current flow. 245 583 if (i==66) 246 Iapd *= 1.3;584 Iapd /= 1.3; 247 585 if (i==191 || i==193) 248 Iapd *= 1.4; 249 250 // If the G-APD voltage is above the breakdown voltage we have the current through the 251 // G-APD and the over-voltage applied to the G-APD to calculate its differential resistor. 252 if (Iapd>0) 586 Iapd /= 1.4; 587 588 // The differential resistance of the G-APD, i.e. the dependence of the 589 // current above the breakdown voltage, is given by 590 //const double Rapd = Uov/Iapd; 591 // This allows us to estimate the current Iov at the overvoltage we want to apply 592 //const double Iov = pow(overvoltage, 1)/Rapd; 593 594 // Estimate set point for over-voltage (voltage drop at the target point) 595 //const double Uset = Ubd + overvoltage + R*Iov*N; 596 double Uset = Uov<0.3 ? Ubd + overvoltage + Udrp : Ubd + overvoltage + Udrp*pow(overvoltage/Uov, 1.66); 597 598 // Voltage set point 599 vec[i] = Uset; 600 601 // Calculate statistics only for channels with a valid calibration 602 //if (Uov>0) 253 603 { 254 // The differential resistance of the G-APD, i.e. the dependence of the 255 // current above the breakdown voltage, is given by 256 const double Rapd = Uov/Iapd; 257 258 // This allows us to estimate the current Iov at the overvoltage we want to apply 259 const double Iov = (1.1+fBiasOffset)/Rapd; 260 261 // Estimate set point for over-voltage 262 const double Uset = (1.1+fBiasOffset) + Ubd + R*Iov*N; 263 264 // Voltage set point as a difference between breakdown voltage and set point 265 Uoff[i] = Uset - fBiasVolt[i]; 604 const int g = hv.group(); 605 606 med[g][num[g]] = Uov; 607 avg[g] += Uov; 608 num[g]++; 609 610 if (Uov<min[g]) 611 min[g] = Uov; 612 if (Uov>max[g]) 613 max[g] = Uov; 614 615 const double iapd = Iapd*1e6; // A --> uA 616 617 data.I[i] = iapd; 618 data.Iavg += iapd; 619 data.Irms += iapd*iapd; 620 621 data.Uov[i] = Uov; 622 623 med[2][num[2]++] = iapd; 266 624 } 267 268 // Calculate statistics only for channels with a valid calibration269 if (Uov>0)270 {271 const int g = hv.group();272 273 med[g][num[g]] = Uov;274 avg[g] += Uov;275 num[g]++;276 277 if (Uov<min[g])278 min[g] = Uov;279 if (Uov>max[g])280 max[g] = Uov;281 }282 625 } 283 626 } 627 628 // ------------------------------- Update voltages ------------------------------------ 629 630 if (GetCurrentState()!=Feedback::State::kCalibrated) 631 { 632 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_VOLTAGE", 633 vec.data(), BIAS::kNumChannels*sizeof(float)); 634 635 ostringstream msg; 636 msg << setprecision(4) << "Sending new absolute offset: dU(" << fTemp << "degC)=" << fTempOffset << "V, Unom=" << overvoltage << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0); 637 Info(msg); 638 } 639 else 640 { 641 ostringstream msg; 642 msg << setprecision(4) << "Current status: dU(" << fTemp << "degC)=" << fTempOffset << "V, Unom=" << overvoltage << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0); 643 Info(msg); 644 645 } 646 647 // --------------------------------- Console out -------------------------------------- 648 649 if (num[0]>0 && num[1]>0 && fIsVerbose) 650 { 284 651 sort(med[0].begin(), med[0].begin()+num[0]); 285 652 sort(med[1].begin(), med[1].begin()+num[1]); 286 653 287 fCurrentsAvg.assign(BIAS::kNumChannels, 0); 288 fCursorCur = 0; 289 } 290 291 fDimDeviation.setQuality(fControlType); 292 fDimDeviation.Update(vec); 293 294 // Warning: Here it is assumed that the ramp up and down is done properly 295 // within the time between two new temperatures and that the calibration 296 // is finished within that time. 297 if (GetCurrentState()!=Feedback::State::kCalibrating || 298 fDimBias.state()!=BIAS::State::kVoltageOff || 299 fCursorTemp!=1 || !fOutputEnabled) 300 { 301 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn) 302 return GetCurrentState(); 303 304 // Trigger calibration 305 if (GetCurrentState()==Feedback::State::kCalibrating && fCursorTemp==2) 306 { 307 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); 308 return GetCurrentState(); 309 } 310 } 311 312 ostringstream msg; 313 msg << setprecision(4) << "Sending new absolute offset (" << fAppliedOffset << "V+" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << "V) to biasctrl."; 314 Info(msg); 315 316 if (fControlType==kCurrents && num[0]>0 && num[1]>0) 317 { 318 msg.str(""); 654 ostringstream msg; 319 655 msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1]; 320 656 Debug(msg); … … 333 669 } 334 670 335 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_OFFSET", 336 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float)); 337 338 return GetCurrentState(); 339 } 340 341 int HandleCurrentControl() 342 { 343 const double dUt = fTempOffset; // [V] 344 345 if (GetCurrentState()==Feedback::State::kCalibrating && fBiasOffset>dUt-1.2) 346 { 347 fCursorTemp = 0; 348 349 ostringstream msg; 350 msg << " (applied calibration offset " << fBiasOffset << "V exceeds temperature correction " << fTempOffset << "V - 1.2V."; 351 Warn("Trying to calibrate above G-APD breakdown volatge!"); 352 Warn(msg); 353 return GetCurrentState(); 354 } 355 356 // FIXME: If calibrating do not wait for the temperature! 357 fAppliedOffset = fBiasOffset; 358 if (GetCurrentState()!=Feedback::State::kCalibrating) 359 fAppliedOffset += dUt; 360 361 vector<float> vec(2*BIAS::kNumChannels+2); 362 for (int i=0; i<BIAS::kNumChannels; i++) 363 vec[i+BIAS::kNumChannels] = fAppliedOffset; 364 365 vec[BIAS::kNumChannels*2] = dUt; 366 vec[BIAS::kNumChannels*2+1] = fBiasOffset; 367 368 double avg[2] = { 0, 0 }; 369 double min[2] = { 90, 90 }; 370 double max[2] = { -90, -90 }; 371 int num[2] = { 0, 0 }; 372 373 vector<double> med[2]; 374 med[0].resize(416); 375 med[1].resize(416); 376 377 if (fControlType==kCurrents) 378 { 379 if (fCursorCur==0) 380 { 381 //DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); 382 return GetCurrentState(); 383 } 384 385 // Pixel 583: 5 31 == 191 (5) C2 B3 P3 386 // Pixel 830: 2 2 == 66 (4) C0 B8 P1 387 // Pixel 1401: 6 1 == 193 (5) C2 B4 P0 388 389 // Convert from DAC counts to uA 390 const double conv = 5000./4096; 391 392 // 3900 Ohm/n + 1000 Ohm + 1100 Ohm (with n=4 or n=5) 393 const double R[2] = { 3075, 2870 }; 394 395 const float *Iavg = fCalibration.data(); // Offset at U=fCalibrationOffset 396 const float *Ravg = fCalibration.data()+BIAS::kNumChannels*2; // Measured resistance 397 398 // U0 = fCalibrationOffset 399 // dT = fAppliedVoltage 400 401 // Ifeedback = Im[i] - (U[i]-U0)/Ravg[i] - Iavg[i]; 402 // dUapplied[i] + dUneu[i] = R[g] * (Im[i] - (dUapplied[i]+dUneu[i]-U0+dT)/Ravg[i] - Iavg[i]) 403 404 // The assumption here is that the offset calculated from the temperature 405 // does not significanly change within a single step 406 407 // dU[i] := dUtotal[i] = dUapplied[i] + dUneu[i] 408 // dU[i] / R[g] = Im[i] - (dU[i]+dT-U0)/Ravg[i] - Iavg[i] 409 // dU[i]/R[g] + dU[i]/Ravg[i] = Im[i] + U0/Ravg[i] - dT/Ravg[i] - Iavg[i] 410 // dU[i]*(1/R[g]+1/Ravg[i]) = Im[i] - Iavg[i] + U0/Ravg[i] - dT/Ravg[i] 411 // dU[i] = (Im[i] - Iavg[i] + U0/Ravg[i] - dT/Ravg[i]) / (1/R[g]+1/Ravg[i]) 412 // dU[i] = { Im[i] - Iavg[i] + (U0-dT)/Ravg[i] } * r with r := 1 / (1/R[g]+1/Ravg[i]) 413 414 const double U0 = fAppliedOffset-fCalibrationOffset; 415 416 for (int i=0; i<BIAS::kNumChannels; i++) 417 { 418 const PixelMapEntry &hv = fMap.hv(i); 419 if (!hv) 420 continue; 421 422 // Average measured current 423 const double Im = double(fCurrentsAvg[i])/fCursorCur * conv; // [uA] 424 425 // Group index (0 or 1) of the of the pixel (4 or 5 pixel patch) 426 const int g = hv.group(); 427 428 // Serial resistors in front of the G-APD 429 double Rg = R[g]; 430 431 // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor 432 if (i==66) // Pixel 830(66) 433 Rg = 2400; // 2400 = (3/3900 + 1/390) + 1000 + 1100 434 if (i==191 || i==193) // Pixel 583(191) / Pixel 1401(193) 435 Rg = 2379; // 2379 = (4/3900 + 1/390) + 1000 + 1100 436 437 const double r = 1./(1./Rg + 1./Ravg[i]); // [Ohm] 438 439 // Offset induced by the voltage above the calibration point 440 const double dI = U0/Ravg[i]; // [V/Ohm] 441 442 // Offset at the calibration point (make sure that the calibration is 443 // valid (Im[i]>Iavg[i]) and we operate above the calibration point) 444 const double I = Im>Iavg[i] ? Im - Iavg[i] : 0; // [A] 445 446 // Make sure that the averaged resistor is valid 447 const double dU = Ravg[i]>10000 ? r*(I*1e-6 - dI) : 0; 448 449 vec[i+BIAS::kNumChannels] += dU; 450 451 // Angelegte Spannung: U0+dU 452 // Gemessener Strom: Im - Iavg 453 // Strom offset: (U0+dU) / Ravg 454 // Fliessender Strom: Im-Iavg - (U0+dU)/Ravg 455 // Korrektur: [ Im-Iavg - (U0+dU)/Ravg ] * Rg 456 457 // Aufgeloest nach dU: dU = ( Im-Iavg - dU/Ravg ) / ( 1/Rg + 1/Ravg ) 458 // Equivalent zu: dU = ( I*Ravg - U0 ) / ( Ravg/Rg+1 ) 459 460 // Calculate statistics only for channels with a valid calibration 461 if (Iavg[i]>0) 462 { 463 med[g][num[g]] = dU; 464 avg[g] += dU; 465 num[g]++; 466 467 if (dU<min[g]) 468 min[g] = dU; 469 if (dU>max[g]) 470 max[g] = dU; 471 } 472 } 473 474 sort(med[0].begin(), med[0].begin()+num[0]); 475 sort(med[1].begin(), med[1].begin()+num[1]); 476 477 fCurrentsAvg.assign(BIAS::kNumChannels, 0); 478 fCursorCur = 0; 479 } 480 481 fDimDeviation.setQuality(fControlType); 482 fDimDeviation.Update(vec); 483 484 // Warning: Here it is assumed that the ramp up and down is done properly 485 // within the time between two new temperatures and that the calibration 486 // is finished within that time. 487 if (!(GetCurrentState()==Feedback::State::kCalibrating && fCursorTemp==1 && fOutputEnabled && fDimBias.state()==BIAS::State::kVoltageOff)) 488 { 489 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn) 490 return GetCurrentState(); 491 492 // Trigger calibration 493 if (GetCurrentState()==Feedback::State::kCalibrating && fCursorTemp==2) 494 { 495 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); 496 return GetCurrentState(); 497 } 498 } 499 500 ostringstream msg; 501 msg << setprecision(4) << "Sending new absolute offset (" << fAppliedOffset << "V+" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << "V) to biasctrl."; 502 Info(msg); 503 504 if (fControlType==kCurrents && num[0]>0 && num[1]>0) 505 { 506 msg.str(""); 507 msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1]; 508 Debug(msg); 509 510 msg.str(""); 511 msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2]; 512 Debug(msg); 513 514 msg.str(""); 515 msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1]; 516 Debug(msg); 517 518 msg.str(""); 519 msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1]; 520 Debug(msg); 521 } 522 523 DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_OFFSET", 524 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float)); 525 526 return GetCurrentState(); 527 } 528 529 int AverageCurrents(const EventImp &evt) 530 { 531 if (evt.GetSize()!=BIAS::kNumChannels*sizeof(int16_t)) 532 return -1; 533 534 if (fDimBias.state()!=BIAS::State::kVoltageOn) 535 return false; 536 537 if (fCursorCur++<0) 538 return true; 539 540 const int16_t *ptr = evt.Ptr<int16_t>(); 541 542 for (int i=0; i<BIAS::kNumChannels; i++) 543 { 544 fCurrentsAvg[i] += ptr[i]; 545 fCurrentsRms[i] += ptr[i]*ptr[i]; 546 } 547 548 return true; 549 } 550 551 552 void HandleCalibration(const EventImp &evt) 553 { 554 const int rc = AverageCurrents(evt); 555 if (rc<0) 556 return; 557 558 if (fCursorCur<fNumCalibRequests) 559 { 560 if (fDimBias.state()==BIAS::State::kVoltageOn) 561 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); 562 return; 563 } 564 565 if (rc==0) 566 return; 567 568 fCalibration.resize(BIAS::kNumChannels*3); 569 570 float *avg = fCalibration.data(); 571 float *rms = fCalibration.data()+BIAS::kNumChannels; 572 float *res = fCalibration.data()+BIAS::kNumChannels*2; 573 574 const double conv = 5000./4096; 575 576 for (int i=0; i<BIAS::kNumChannels; i++) 577 { 578 const double I = double(fCurrentsAvg[i])/fCursorCur; 579 580 res[i] = (fVoltGapd[i]+fCalibrationOffset)/I / conv * 1e6; 581 avg[i] = conv * I; 582 rms[i] = conv * sqrt(double(fCurrentsRms[i])/fCursorCur-I*I); 583 } 584 585 fCalibTime = Time(); 586 587 fDimCalibration.setData(fCalibration); 588 fDimCalibration.Update(fCalibTime); 589 590 fOutputEnabled = false; 591 fControlType = kIdle; 592 593 Info("Calibration successfully done."); 594 595 if (fDimBias.state()==BIAS::State::kVoltageOn) 596 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0); 597 } 598 599 void HandleFeedback(const EventImp &evt) 600 { 601 if (evt.GetSize()!=1440*sizeof(float)) 602 return; 603 604 // -------- Check age of last stored event -------- 605 606 const Time tm(evt.GetTime()); 607 608 if (Time()-fBiasLast>boost::posix_time::seconds(30)) 609 { 610 Warn("Last received event data older than 30s... resetting average calculation."); 611 ResetData(); 612 } 613 fBiasLast = tm; 614 615 // -------- Store new event -------- 616 617 fData[fCursorAmpl%fData.size()].assign(evt.Ptr<float>(), evt.Ptr<float>()+1440); 618 if (++fCursorAmpl<fData.size()) 619 return; 620 621 // -------- Calculate statistics -------- 622 623 valarray<double> med(1440); 624 625 for (int ch=0; ch<1440; ch++) 626 { 627 vector<float> arr(fData.size()); 628 for (size_t i=0; i<fData.size(); i++) 629 arr[i] = fData[i][ch]; 630 631 sort(arr.begin(), arr.end()); 632 633 med[ch] = arr[arr.size()/2]; 634 } 635 636 /* 637 vector<float> med(1440); 638 vector<float> rms(1440); 639 for (size_t i=0; i<fData.size(); i++) 640 { 641 if (fData[i].size()==0) 642 return; 643 644 for (int j=0; j<1440; j++) 645 { 646 med[j] += fData[i][j]; 647 rms[j] += fData[i][j]*fData[i][j]; 648 } 649 } 650 */ 651 652 vector<double> avg(BIAS::kNumChannels); 653 vector<int> num(BIAS::kNumChannels); 654 for (int i=0; i<1440; i++) 655 { 656 const PixelMapEntry &ch = fMap.hw(i); 657 658 // FIXME: Add a consistency check if the median makes sense... 659 // FIXME: Add a consistency check to remove pixels with bright stars (median?) 660 661 avg[ch.hv()] += med[i]; 662 num[ch.hv()]++; 663 } 664 665 for (int i=0; i<BIAS::kNumChannels; i++) 666 { 667 if (num[i]) 668 avg[i] /= num[i]; 669 670 } 671 672 // -------- Calculate correction -------- 673 674 // http://bestune.50megs.com/typeABC.htm 675 676 // CO: Controller output 677 // PV: Process variable 678 // SP: Set point 679 // T: Sampling period (loop update period) 680 // e = SP - PV 681 // 682 // Kp : No units 683 // Ki : per seconds 684 // Kd : seconds 685 686 // CO(k)-CO(k-1) = - Kp[ PV(k) - PV(k-1) ] + Ki * T * (SP(k)-PV(k)) - Kd/T [ PV(k) - 2PV(k-1) + PV(k-2) ] 687 688 if (fCursorAmpl%fData.size()>0) 689 return; 690 691 // FIXME: Take out broken / dead boards. 692 693 const Time tm0 = Time(); 694 695 /*const*/ double T21 = fT>0 ? fT : (tm0-fStartTime).total_microseconds()/1000000.; 696 const double T10 = fT21; 697 fT21 = T21; 698 699 fStartTime = tm0; 700 701 ostringstream out; 702 out << "New " << fData.size() << " event received: " << fCursorAmpl << " / " << setprecision(3) << T21 << "s"; 703 Info(out); 704 705 if (fPV[0].size()==0) 706 { 707 fPV[0].resize(avg.size()); 708 fPV[0] = valarray<double>(avg.data(), avg.size()); 709 return; 710 } 711 712 if (fPV[1].size()==0) 713 { 714 fPV[1].resize(avg.size()); 715 fPV[1] = valarray<double>(avg.data(), avg.size()); 716 return; 717 } 718 719 if (fPV[2].size()==0) 720 { 721 fPV[2].resize(avg.size()); 722 fPV[2] = valarray<double>(avg.data(), avg.size()); 723 return; 724 } 725 726 fPV[0] = fPV[1]; 727 fPV[1] = fPV[2]; 728 729 fPV[2].resize(avg.size()); 730 fPV[2] = valarray<double>(avg.data(), avg.size()); 731 732 if (T10<=0 || T21<=0) 733 return; 734 735 //cout << "Calculating (" << fCursor << ":" << T21 << ")... " << endl; 736 737 // fKi[j] = response[j]*gain; 738 // Kp = 0; 739 // Kd = 0; 740 741 // => Kp = 0.01 * gain = 0.00005 742 // => Ki = 0.8 * gain/20s = 0.00025 743 // => Kd = 0.1 * gain/20s = 0.00003 744 745 /* 746 fKp = 0; 747 fKd = 0; 748 fKi = 0.00003*20; 749 T21 = 1; 750 */ 751 752 //valarray<double> correction = - Kp*(PV[2] - PV[1]) + Ki * dT * (SP-PV[2]) - Kd/dT * (PV[2] - 2*PV[1] + PV[0]); 753 //valarray<double> correction = 754 // - Kp * (PV[2] - PV[1]) 755 // + dT * Ki * (SP - PV[2]) 756 // - Kd / dT * (PV[2] - 2*PV[1] + PV[0]); 757 // 758 // - (Kp+Kd/dT1) * (PV[2] - PV[1]) 759 // + dT2 * Ki * (SP - PV[2]) 760 // + Kd / dT1 * (PV[1] - PV[0]); 761 // 762 // - Kp * (PV[2] - PV[1]) 763 // + Ki * (SP - PV[2])*dT 764 // - Kd * (PV[2] - PV[1])/dT 765 // + Kd * (PV[1] - PV[0])/dT; 766 // 767 //valarray<double> correction = 768 // - Kp*(PV[2] - PV[1]) + Ki * T21 * (SP-PV[2]) - Kd*(PV[2]-PV[1])/T21 - Kd*(PV[0]-PV[1])/T01; 769 const valarray<double> correction = 1./fGain/1000* 770 ( 771 - (fKp+fKd/T21)*(fPV[2] - fPV[1]) 772 + fKi*T21*(fSP-fPV[2]) 773 + fKd/T10*(fPV[1]-fPV[0]) 774 ); 775 776 /* 777 integral = 0 778 start: 779 integral += (fSP - fPV[2])*dt 780 781 output = Kp*(fSP - fPV[2]) + Ki*integral - Kd*(fPV[2] - fPV[1])/dt 782 783 wait(dt) 784 785 goto start 786 */ 787 788 vector<float> vec(2*BIAS::kNumChannels+2); 789 for (int i=0; i<BIAS::kNumChannels; i++) 790 vec[i] = fPV[2][i]-fSP[i]; 791 792 for (int i=0; i<BIAS::kNumChannels; i++) 793 vec[i+BIAS::kNumChannels] = avg[i]<5*2.5 ? 0 : correction[i]; 794 795 fDimDeviation.setQuality(fControlType); 796 fDimDeviation.Update(vec); 797 798 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn) 799 return; 800 801 Info("Sending new relative offset to biasctrl."); 802 803 DimClient::sendCommandNB("BIAS_CONTROL/INCREASE_ALL_CHANNELS_VOLTAGE", 804 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float)); 805 } 806 807 void HandleGlobalFeedback(const EventImp &evt) 808 { 809 if (evt.GetSize()!=1440*sizeof(float)) 810 return; 811 812 // -------- Store new event -------- 813 814 vector<float> arr(evt.Ptr<float>(), evt.Ptr<float>()+1440); 815 816 sort(arr.begin(), arr.end()); 817 818 const float med = arr[arr.size()/2]; 819 820 fData[fCursorAmpl%fData.size()].resize(1); //assign(&med, &med); 821 fData[fCursorAmpl%fData.size()][0] = med; //assign(&med, &med); 822 823 if (++fCursorAmpl<fData.size()) 824 return; 825 826 // -------- Calculate statistics -------- 827 828 double avg=0; 829 double rms=0; 830 for (size_t i=0; i<fData.size(); i++) 831 { 832 avg += fData[i][0]; 833 rms += fData[i][0]*fData[i][0]; 834 } 835 836 avg /= fData.size(); 837 rms /= fData.size(); 838 839 rms = sqrt(rms-avg*avg); 840 841 // -------- Calculate correction -------- 842 843 if (fCursorAmpl%fData.size()!=0) 844 return; 845 846 Out() << "Amplitude: " << avg << " +- " << rms << endl; 847 848 // FIXME: Take out broken / dead boards. 849 850 /* 851 ostringstream out; 852 out << "New " << fData.size() << " event received: " << fCursor << " / " << setprecision(3) << T21 << "s"; 853 Info(out); 854 */ 855 856 if (fPV[0].size()==0) 857 { 858 fPV[0].resize(1); 859 fPV[0] = valarray<double>(&avg, 1); 860 return; 861 } 862 863 if (fPV[1].size()==0) 864 { 865 fPV[1].resize(1); 866 fPV[1] = valarray<double>(&avg, 1); 867 return; 868 } 869 870 if (fPV[2].size()==0) 871 { 872 fPV[2].resize(1); 873 fPV[2] = valarray<double>(&avg, 1); 874 return; 875 } 876 877 fPV[0] = fPV[1]; 878 fPV[1] = fPV[2]; 879 880 fPV[2].resize(1); 881 fPV[2] = valarray<double>(&avg, 1); 882 883 // ----- Calculate average currents ----- 884 885 vector<float> A(BIAS::kNumChannels); 886 for (int i=0; i<BIAS::kNumChannels; i++) 887 A[i] = double(fCurrentsAvg[i]) / fCursorCur; 888 889 fCurrentsAvg.assign(BIAS::kNumChannels, 0); 890 fCursorCur = 0; 891 892 // -------- Calculate correction -------- 893 894 // correction = (fSP[0]-fPV[2])*fKi 895 /* 896 const double T21 = 1; // feedback is 1s 897 const double T10 = 1; // feedback is 20s 898 899 const valarray<double> correction = 1./fGain/1000* 900 ( 901 - (fKp+fKd/T21)*(fPV[2] - fPV[1]) 902 + fKi*T21*(fSP[0]-fPV[2]) 903 + fKd/T10*(fPV[1]-fPV[0]) 904 ); 905 */ 906 907 // pow of 1.6 comes from the non-linearity of the 908 // amplitude vs bias voltage 909 const valarray<double> correction = 1./fGain/1000* 910 ( 911 //fKi*(pow(fSP[0], 1./1.6)-pow(fPV[2], 1./1.6)) 912 fKi*(fSP[0]-fPV[2]) 913 ); 914 915 Out() << "Correction: " << correction[0] << "V (" << fSP[0] << ")" << endl; 916 917 const int nch = BIAS::kNumChannels; 918 919 // FIXME: Sanity check! 920 921 vector<float> vec; 922 vec.reserve(2*nch+2); 923 vec.insert(vec.begin(), nch, fPV[2][0]-fSP[0]); 924 vec.insert(vec.begin()+nch, nch, correction[0]); 925 vec.push_back(0); 926 vec.push_back(0); 927 928 fDimDeviation.setQuality(fControlType); 929 fDimDeviation.Update(vec); 930 931 if (!fOutputEnabled || fDimBias.state()!=BIAS::State::kVoltageOn) 932 return; 933 934 Info("Sending new global relative offset to biasctrl."); 935 936 DimClient::sendCommandNB("BIAS_CONTROL/INCREASE_ALL_CHANNELS_VOLTAGE", 937 vec.data()+BIAS::kNumChannels, BIAS::kNumChannels*sizeof(float)); 938 } 939 940 void HandleCalibrateCurrents(const EventImp &evt) 941 { 942 if (fBiasVolt.empty() || fCalibration.empty() || evt.GetSize()<416*sizeof(int16_t)) 943 return; 944 945 struct dim_data { 946 float I[416]; 947 float Iavg; 948 float Irms; 949 float Imed; 950 float Idev; 951 uint32_t N; 952 float Tdiff; 953 954 dim_data() { memset(this, 0, sizeof(dim_data)); } 955 } __attribute__((__packed__)); 956 957 const int16_t *I = evt.Ptr<int16_t>(); 958 const float *R = fCalibration.data()+BIAS::kNumChannels*2; 959 const float *U = fBiasVolt.data(); 960 961 vector<float> med(416); 962 uint16_t cnt = 0; 963 964 double avg = 0; 965 double rms = 0; 966 967 dim_data data; 968 for (int i=0; i<416; i++) 969 { 970 const PixelMapEntry &hv = fMap.hv(i); 971 if (!hv) 972 continue; 973 974 if (R[i]<=0) 975 continue; 976 977 data.I[i] = I[i]*5000./4096 - U[i]/R[i]*1e6; 978 data.I[i] /= hv.group() ? 5 : 4; 979 980 avg += data.I[i]; 981 rms += data.I[i]*data.I[i]; 982 983 if (i>=320) 984 continue; 985 986 med[cnt++] = data.I[i]; 987 } 988 989 if (cnt==0) 990 return; 991 992 avg /= cnt; 993 rms /= cnt; 994 995 data.N = cnt; 996 data.Iavg = avg; 997 data.Irms = sqrt(rms-avg*avg); 998 999 sort(med.data(), med.data()+cnt); 1000 1001 data.Imed = cnt%2 ? (med[cnt/2-1]+med[cnt/2])/2 : med[cnt/2]; 1002 1003 for (int i=0; i<cnt; i++) 1004 med[i] = fabs(med[i]-data.Imed); 1005 1006 sort(med.data(), med.data()+cnt); 1007 1008 data.Idev = med[uint32_t(0.682689477208650697*cnt)]; 1009 1010 data.Tdiff = evt.GetTime().UnixTime()-fCalibTime.UnixTime(); 1011 1012 fDimCurrents.setData(&data, sizeof(dim_data)); 1013 fDimCurrents.Update(evt.GetTime()); 1014 } 1015 1016 int HandleBiasCurrent(const EventImp &evt) 1017 { 1018 if (fControlType==kTemp && GetCurrentState()==Feedback::State::kCalibrating) 1019 HandleCalibration(evt); 1020 1021 if (fControlType==kFeedbackGlobal || fControlType==kCurrents || fControlType==kCurrentsNew) 1022 AverageCurrents(evt); 1023 1024 /* 1025 if (fControlType==kCurrents && fCursorTemp>0 && fCursorCur>0) 1026 { 1027 // fCursorTemp: 1 2 3 4 5 6 7 8 1028 // fCursor%x: 1 1 1 2 2 2 3 3 // 9 steps in ~15s 1029 //if (fCursorTemp<3 && fCursorCur%(fCursorTemp/3+1)==0) 1030 HandleCurrentControl(); 1031 }*/ 1032 1033 HandleCalibrateCurrents(evt); 1034 1035 return GetCurrentState(); 1036 } 1037 1038 int HandleBiasData(const EventImp &evt) 1039 { 1040 if (fControlType==kFeedback) 1041 HandleFeedback(evt); 1042 1043 if (fControlType==kFeedbackGlobal) 1044 HandleGlobalFeedback(evt); 1045 1046 return GetCurrentState(); 1047 } 1048 1049 int HandleBiasNom(const EventImp &evt) 1050 { 1051 if (evt.GetSize()>=416*sizeof(float)) 1052 { 1053 fVoltGapd.assign(evt.Ptr<float>(), evt.Ptr<float>()+416); 1054 Info("Nominal bias voltages received."); 1055 } 1056 1057 return GetCurrentState(); 1058 } 1059 1060 int HandleBiasVoltage(const EventImp &evt) 1061 { 1062 if (evt.GetSize()>=416*sizeof(float)) 1063 fBiasVolt.assign(evt.Ptr<float>(), evt.Ptr<float>()+416); 1064 return GetCurrentState(); 1065 } 1066 1067 bool CheckEventSize(size_t has, const char *name, size_t size) 1068 { 1069 if (has==size) 1070 return true; 1071 1072 ostringstream msg; 1073 msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; 1074 Fatal(msg); 1075 return false; 1076 } 671 // ---------------------------- Calibrated Currents ----------------------------------- 672 673 if (num[2]>0) 674 { 675 data.Iavg /= num[2]; 676 data.Irms /= num[2]; 677 678 data.N = num[2]; 679 data.Irms = sqrt(data.Irms-data.Iavg*data.Iavg); 680 681 sort(med[2].data(), med[2].data()+num[2]); 682 683 data.Imed = num[2]%2 ? (med[2][num[2]/2-1]+med[2][num[2]/2])/2 : med[2][num[2]/2]; 684 685 for (int i=0; i<num[2]; i++) 686 med[2][i] = fabs(med[2][i]-data.Imed); 687 688 sort(med[2].data(), med[2].data()+num[2]); 689 690 data.Idev = med[2][uint32_t(0.682689477208650697*num[2])]; 691 692 data.Tdiff = evt.GetTime().UnixTime()-fTimeCalib.UnixTime(); 693 694 // FIXME: 695 // + Current overvoltage 696 // + Temp offset 697 // + User offset 698 // + Command overvoltage 699 fDimCurrents.setQuality(GetCurrentState()); 700 fDimCurrents.setData(&data, sizeof(dim_data)); 701 fDimCurrents.Update(evt.GetTime()); 702 } 703 704 return GetCurrentState()==Feedback::State::kCalibrated ? Feedback::State::kCalibrated : Feedback::State::kInProgress; 705 } 706 707 // ====================================================================== 1077 708 1078 709 int Print() const 1079 710 { 1080 711 Out() << fDim << endl; 1081 Out() << fDimFAD << endl;1082 712 Out() << fDimFSC << endl; 1083 713 Out() << fDimBias << endl; … … 1088 718 int PrintCalibration() 1089 719 { 1090 if (fCalibration.empty()) 720 /* 721 if (fCalibration.size()==0) 1091 722 { 1092 723 Out() << "No calibration performed so far." << endl; … … 1121 752 1122 753 Out() << flush; 1123 754 */ 1124 755 return GetCurrentState(); 1125 756 } 1126 757 1127 void WarnState(bool needfsc, bool needfad) 1128 { 758 int EnableOldAlgorithm(const EventImp &evt) 759 { 760 if (!CheckEventSize(evt.GetSize(), "EnableOldAlgorithm", 1)) 761 return kSM_FatalError; 762 763 fEnableOldAlgorithm = evt.GetBool(); 764 765 return GetCurrentState(); 766 } 767 768 int SetVerbosity(const EventImp &evt) 769 { 770 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1)) 771 return kSM_FatalError; 772 773 fIsVerbose = evt.GetBool(); 774 775 return GetCurrentState(); 776 } 777 778 int SetCurrentRequestInterval(const EventImp &evt) 779 { 780 if (!CheckEventSize(evt.GetSize(), "SetCurrentRequestInterval", 2)) 781 return kSM_FatalError; 782 783 fCurrentRequestInterval = evt.GetUShort(); 784 785 Out() << "New current request interval: " << fCurrentRequestInterval << "ms" << endl; 786 787 return GetCurrentState(); 788 } 789 790 int Calibrate() 791 { 792 if (fDimBias.state()!=BIAS::State::kVoltageOff) 793 { 794 Warn("Calibration can only be started when biasctrl is in state VoltageOff."); 795 return GetCurrentState(); 796 } 797 798 Message("Starting calibration (ignore="+to_string(fNumCalibIgnore)+", N="+to_string(fNumCalibRequests)+")"); 799 800 fCursorCur = -fNumCalibIgnore; 801 fCurrentsAvg.assign(BIAS::kNumChannels, 0); 802 fCurrentsRms.assign(BIAS::kNumChannels, 0); 803 804 fBiasDac.assign(BIAS::kNumChannels, 0); 805 806 fCalibStep = 3; 807 fTimeCalib = Time(); 808 809 Dim::SendCommandNB("BIAS_CONTROL/SET_GLOBAL_DAC", uint32_t(256+512*fCalibStep)); 810 811 return Feedback::State::kCalibrating; 812 } 813 814 int Start(const EventImp &evt) 815 { 816 if (!CheckEventSize(evt.GetSize(), "Start", 4)) 817 return kSM_FatalError; 818 819 fUserOffset = evt.GetFloat(); 820 821 fCursorCur = 0; 822 823 fCurrentsAvg.assign(BIAS::kNumChannels, 0); 824 fCurrentsRms.assign(BIAS::kNumChannels, 0); 825 826 ostringstream out; 827 out << "Starting feedback with an offset of " << fUserOffset << "V"; 828 Message(out); 829 830 return Feedback::State::kWaitingForData; 831 } 832 833 int StopFeedback() 834 { 835 if (GetCurrentState()==Feedback::State::kCalibrating) 836 return Feedback::State::kConnected; 837 838 if (GetCurrentState()>Feedback::State::kCalibrated) 839 return Feedback::State::kCalibrated; 840 841 return GetCurrentState(); 842 } 843 844 int Execute() 845 { 846 if (!fDim.online()) 847 return Feedback::State::kDimNetworkNA; 848 1129 849 const bool bias = fDimBias.state() >= BIAS::State::kConnecting; 1130 850 const bool fsc = fDimFSC.state() >= FSC::State::kConnected; 1131 const bool fad = fDimFAD.state() >= FAD::State::kConnected; 1132 1133 if (!bias) 1134 Warn("Bias control not yet ready."); 1135 if (needfsc && !fsc) 1136 Warn("FSC control not yet ready."); 1137 if (needfad && !fad) 1138 Warn("FAD control not yet ready."); 1139 } 1140 1141 int SetConstant(const EventImp &evt, int constant) 1142 { 1143 if (!CheckEventSize(evt.GetSize(), "SetConstant", 8)) 1144 return kSM_FatalError; 1145 1146 switch (constant) 1147 { 1148 case 0: fKi = evt.GetDouble(); break; 1149 case 1: fKp = evt.GetDouble(); break; 1150 case 2: fKd = evt.GetDouble(); break; 1151 case 3: fT = evt.GetDouble(); break; 1152 case 4: fGain = evt.GetDouble(); break; 1153 default: 1154 Fatal("SetConstant got an unexpected constant id -- this is a program bug!"); 1155 return kSM_FatalError; 851 852 // All subsystems are not connected 853 if (!bias && !fsc) 854 return Feedback::State::kDisconnected; 855 856 // Not all subsystems are yet connected 857 if (!bias || !fsc) 858 return Feedback::State::kConnecting; 859 860 if (GetCurrentState()<Feedback::State::kCalibrating) 861 return Feedback::State::kConnected; 862 863 if (GetCurrentState()==Feedback::State::kConnected) 864 return GetCurrentState(); 865 if (GetCurrentState()==Feedback::State::kCalibrating) 866 return GetCurrentState(); 867 868 // kCalibrated, kWaitingForData, kInProgress 869 870 if (fDimBias.state()==BIAS::State::kVoltageOn || (fDimBias.state()==BIAS::State::kVoltageOff && GetCurrentState()==Feedback::State::kWaitingForData)) 871 { 872 static Time past; 873 if (fCurrentRequestInterval>0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval)) 874 { 875 Dim::SendCommandNB("BIAS_CONTROL/REQUEST_STATUS"); 876 past = Time(); 877 } 1156 878 } 1157 879 1158 880 return GetCurrentState(); 1159 }1160 1161 int EnableOutput(const EventImp &evt)1162 {1163 if (!CheckEventSize(evt.GetSize(), "EnableOutput", 1))1164 return kSM_FatalError;1165 1166 fOutputEnabled = evt.GetBool();1167 1168 if (fControlType==kCurrents || fControlType==kCurrentsNew)1169 if (fCursorTemp>1)1170 fCursorTemp = 1;1171 1172 return GetCurrentState();1173 }1174 1175 void ResetData(int16_t n=-1)1176 {1177 fData.assign(n>0 ? n : fData.size(), vector<float>(0));1178 1179 fCursorAmpl = 0;1180 fCursorCur = 0;1181 fCursorTemp = 0;1182 1183 fStartTime = Time();1184 1185 fSP = valarray<double>(0., BIAS::kNumChannels);1186 1187 vector<float> vec(2*BIAS::kNumChannels+2, fBiasOffset);1188 vec[2*BIAS::kNumChannels] = 0;1189 fDimDeviation.setQuality(kIdle);1190 fDimDeviation.Update(vec);1191 1192 fPV[0].resize(0);1193 fPV[1].resize(0);1194 fPV[2].resize(0);1195 1196 fCurrentsAvg.assign(BIAS::kNumChannels, 0);1197 fCurrentsRms.assign(BIAS::kNumChannels, 0);1198 1199 if (fKp==0 && fKi==0 && fKd==0)1200 Warn("Control loop parameters are all set to zero.");1201 }1202 1203 int StartFeedback(const EventImp &evt)1204 {1205 if (!CheckEventSize(evt.GetSize(), "StartFeedback", 2))1206 return kSM_FatalError;1207 1208 WarnState(false, true);1209 1210 fBiasOffset = 0;1211 ResetData(evt.GetShort());1212 1213 fControlType = kFeedback;1214 1215 return GetCurrentState();1216 }1217 1218 int StartFeedbackGlobal(const EventImp &evt)1219 {1220 if (!CheckEventSize(evt.GetSize(), "StartFeedbackGlobal", 2))1221 return kSM_FatalError;1222 1223 WarnState(false, true);1224 1225 fBiasOffset = 0;1226 ResetData(evt.GetShort());1227 1228 fControlType = kFeedbackGlobal;1229 1230 return GetCurrentState();1231 }1232 1233 int StartTempCtrl(const EventImp &evt)1234 {1235 if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))1236 return kSM_FatalError;1237 1238 WarnState(true, false);1239 1240 fBiasOffset = evt.GetFloat();1241 fControlType = kTemp;1242 1243 ostringstream out;1244 out << "Starting temperature feedback with an offset of " << fBiasOffset << "V";1245 Message(out);1246 1247 if (fDimBias.state()==BIAS::State::kVoltageOn)1248 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);1249 1250 return GetCurrentState();1251 }1252 1253 int StartCurrentCtrl(const EventImp &evt)1254 {1255 if (!CheckEventSize(evt.GetSize(), "StartCurrentCtrl", 4))1256 return kSM_FatalError;1257 1258 if (fCalibration.empty())1259 {1260 Warn("Current control needs a bias crate calibration first... command ignored.");1261 return GetCurrentState();1262 }1263 1264 WarnState(true, false);1265 1266 fBiasOffset = evt.GetFloat();1267 fTempOffset = -3;1268 ResetData(0);1269 fControlType = kCurrents;1270 1271 ostringstream out;1272 out << "Starting current/temp feedback with an offset of " << fBiasOffset << "V";1273 Message(out);1274 1275 return GetCurrentState();1276 }1277 1278 int StartNewCurrentCtrl(const EventImp &evt)1279 {1280 if (!CheckEventSize(evt.GetSize(), "StartNewCurrentCtrl", 4))1281 return kSM_FatalError;1282 1283 if (fCalibration.empty())1284 {1285 Warn("Current control needs a bias crate calibration first... command ignored.");1286 return GetCurrentState();1287 }1288 1289 WarnState(true, false);1290 1291 fBiasOffset = evt.GetFloat();1292 fTempOffset = -3;1293 ResetData(0);1294 fControlType = kCurrentsNew;1295 1296 ostringstream out;1297 out << "Starting new current/temp feedback with an offset of " << fBiasOffset << "V";1298 Message(out);1299 1300 return GetCurrentState();1301 }1302 1303 int StopFeedback()1304 {1305 fControlType = kIdle;1306 1307 return GetCurrentState();1308 }1309 1310 int StoreReference()1311 {1312 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())1313 {1314 Warn("No values in memory. Take enough events first!");1315 return GetCurrentState();1316 }1317 1318 // FIXME: Check age1319 1320 if (!fPV[1].size() && !fPV[2].size())1321 fSP = fPV[0];1322 1323 if (!fPV[2].size())1324 fSP = fPV[1];1325 else1326 fSP = fPV[2];1327 1328 vector<float> vec(BIAS::kNumChannels);1329 for (int i=0; i<BIAS::kNumChannels; i++)1330 vec[i] = fSP[i];1331 fDimReference.Update(vec);1332 1333 return GetCurrentState();1334 }1335 1336 int SetReference(const EventImp &evt)1337 {1338 if (!CheckEventSize(evt.GetSize(), "SetReference", 4))1339 return kSM_FatalError;1340 1341 const float val = evt.GetFloat();1342 /*1343 if (!fPV[0].size() && !fPV[1].size() && !fPV[2].size())1344 {1345 Warn("No values in memory. Take enough events first!");1346 return GetCurrentState();1347 }*/1348 1349 vector<float> vec(BIAS::kNumChannels);1350 for (int i=0; i<BIAS::kNumChannels; i++)1351 vec[i] = fSP[i] = val;1352 fDimReference.Update(vec);1353 1354 Out() << "New global reference value: " << val << "mV" << endl;1355 1356 return GetCurrentState();1357 }1358 1359 int CalibrateCurrents()1360 {1361 // if (!CheckEventSize(evt.GetSize(), "StartTempCtrl", 4))1362 // return kSM_FatalError;1363 1364 if (fDimBias.state()==BIAS::State::kRamping)1365 {1366 Warn("Calibration cannot be started when biasctrl is in state Ramping.");1367 return GetCurrentState();1368 }1369 1370 if (fVoltGapd.empty())1371 {1372 Error("No G-APD reference voltages received yet (BIAS_CONTROL/NOMINAL).");1373 return GetCurrentState();1374 }1375 1376 WarnState(true, false);1377 1378 ostringstream out;1379 out << "Starting temperature feedback for calibration with an offset of " << fCalibrationOffset << "V";1380 Message(out);1381 1382 fBiasOffset = fCalibrationOffset;1383 fControlType = kTemp;1384 fCursorCur = -fNumCalibIgnore;1385 fCursorTemp = 0;1386 fCurrentsAvg.assign(BIAS::kNumChannels, 0);1387 fCurrentsRms.assign(BIAS::kNumChannels, 0);1388 fCalibration.resize(0);1389 fStartTime = Time();1390 fOutputEnabled = true;1391 1392 return Feedback::State::kCalibrating;1393 }1394 1395 int SetCurrentRequestInterval(const EventImp &evt)1396 {1397 if (!CheckEventSize(evt.GetSize(), "SetCurrentRequestInterval", 2))1398 return kSM_FatalError;1399 1400 fCurrentRequestInterval = evt.GetUShort();1401 1402 Out() << "New current request interval: " << fCurrentRequestInterval << "ms" << endl;1403 1404 return GetCurrentState();1405 }1406 1407 int Execute()1408 {1409 // Dispatch (execute) at most one handler from the queue. In contrary1410 // to run_one(), it doesn't wait until a handler is available1411 // which can be dispatched, so poll_one() might return with 01412 // handlers dispatched. The handlers are always dispatched/executed1413 // synchronously, i.e. within the call to poll_one()1414 //poll_one();1415 1416 if (!fDim.online())1417 return Feedback::State::kDimNetworkNA;1418 1419 const bool bias = fDimBias.state() >= BIAS::State::kConnecting;1420 const bool fad = fDimFAD.state() >= FAD::State::kConnected;1421 const bool fsc = fDimFSC.state() >= FSC::State::kConnected;1422 1423 // All subsystems are not connected1424 if (!bias && !fad && !fsc)1425 return Feedback::State::kDisconnected;1426 1427 // At least one subsystem apart from bias is connected1428 if (bias && !fad && !fsc)1429 return Feedback::State::kConnecting;1430 1431 /*1432 // All subsystems are connected1433 if (GetCurrentStatus()==Feedback::State::kConfiguringStep1)1434 {1435 if (fCursor<1)1436 return Feedback::State::kConfiguringStep1;1437 1438 if (fCursor==1)1439 {1440 fStartTime = Time();1441 return Feedback::State::kConfiguringStep2;1442 }1443 }1444 if (GetCurrentStatus()==Feedback::State::kConfiguringStep2)1445 {1446 if (fCursor==1)1447 {1448 if ((Time()-fStartTime).total_microseconds()/1000000.<1.5)1449 return Feedback::State::kConfiguringStep2;1450 1451 Dim::SendCommand("BIAS_CONTROL/REQUEST_STATUS");1452 }1453 if (fCursor==2)1454 {1455 1456 int n=0;1457 double avg = 0;1458 for (size_t i=0; i<fCurrents.size(); i++)1459 if (fCurrents[i]>=0)1460 {1461 avg += fCurrents[i];1462 n++;1463 }1464 1465 cout << avg/n << endl;1466 }1467 return Feedback::State::kConnected;1468 }1469 */1470 1471 // Needs connection of FAD and BIAS1472 if (bias && fad)1473 {1474 if (fControlType==kFeedback || fControlType==kFeedbackGlobal)1475 return fOutputEnabled ? Feedback::State::kFeedbackCtrlRunning : Feedback::State::kFeedbackCtrlIdle;1476 }1477 1478 // Needs connection of FSC and BIAS1479 if (bias && fsc)1480 {1481 if (fControlType==kTemp)1482 {1483 if (GetCurrentState()==Feedback::State::kCalibrating && fCursorCur<fNumCalibRequests)1484 return GetCurrentState();1485 1486 return fOutputEnabled ? Feedback::State::kTempCtrlRunning : Feedback::State::kTempCtrlIdle;1487 }1488 if (fControlType==kCurrents || fControlType==kCurrentsNew)1489 {1490 static Time past;1491 if (fCurrentRequestInterval>0 && Time()-past>boost::posix_time::milliseconds(fCurrentRequestInterval))1492 {1493 if (fDimBias.state()==BIAS::State::kVoltageOn)1494 DimClient::sendCommandNB("BIAS_CONTROL/REQUEST_STATUS", NULL, 0);1495 past = Time();1496 }1497 1498 return fOutputEnabled && fCursorTemp>0 ? Feedback::State::kCurrentCtrlRunning : Feedback::State::kCurrentCtrlIdle;1499 }1500 }1501 1502 if (bias && fad && !fsc)1503 return Feedback::State::kConnectedFAD;1504 1505 if (bias && fsc && !fad)1506 return Feedback::State::kConnectedFSC;1507 1508 return Feedback::State::kConnected;1509 881 } 1510 882 1511 883 public: 1512 884 StateMachineFeedback(ostream &out=cout) : StateMachineDim(out, "FEEDBACK"), 885 fIsVerbose(false), fEnableOldAlgorithm(true), 1513 886 //--- 1514 fDimFAD("FAD_CONTROL"),1515 887 fDimFSC("FSC_CONTROL"), 1516 888 fDimBias("BIAS_CONTROL"), 1517 889 //--- 1518 fDimReference("FEEDBACK/REFERENCE", "F:416", 1519 "Amplitude reference value(s)" 1520 "Vref[mV]:Amplitude reference"), 1521 fDimDeviation("FEEDBACK/DEVIATION", "F:416;F:416;F:1;F:1", 1522 "Control loop information" 1523 "|DeltaAmpl[mV]:Amplitude offset measures" 1524 "|DeltaBias[mV]:Correction value calculated" 1525 "|DeltaTemp[mV]:Correction calculated from temperature" 1526 "|DeltaUser[mV]:Additional offset specified by user"), 1527 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416;F:416", 890 fDimCalibration("FEEDBACK/CALIBRATION", "F:416;F:416;F:416;F:416", 1528 891 "Current offsets" 1529 "|Avg[uA]:Average offset" 1530 "|Rms[uA]:Rms of offset" 1531 "|R[Ohm]:Measured calibration resistor"), 1532 fDimCurrents("FEEDBACK/CALIBRATED_CURRENTS", "F:416;F:1;F:1;F:1;F:1;I:1;F:1", 892 "|Avg[uA]:Average offset at dac=256+5*512" 893 "|Rms[uA]:Rms of Avg" 894 "|R[Ohm]:Measured calibration resistor" 895 "|U[V]:Corresponding voltage reported by biasctrl"), 896 fDimCalibration2("FEEDBACK/CALIBRATION_STEPS", "I:1;F:416;F:416;F:416", 897 "Calibration of the R8 resistor" 898 "|DAC[dac]:DAC setting" 899 "|U[V]:Corresponding voltages reported by biasctrl" 900 "|Iavg[uA]:Averaged measured current" 901 "|Irms[uA]:Rms measured current"), 902 fDimCalibrationR8("FEEDBACK/CALIBRATION_R8", "F:416;F:416", 903 "Calibration of R8" 904 "|DeltaI[uA]:Average offset" 905 "|R8[uA]:Measured effective resistor R8"), 906 fDimCurrents("FEEDBACK/CALIBRATED_CURRENTS", "F:416;F:1;F:1;F:1;F:1;I:1;F:1;F:416;F:1;F:1", 1533 907 "Calibrated currents" 1534 "|I[uA]:Calibrated currents "1535 "|I_avg[uA]:Average calibrated current ( 320channels)"1536 "|I_rms[uA]:Rms of calibrated current ( 320channels)"1537 "|I_med[uA]:Median calibrated current ( 320channels)"1538 "|I_dev[uA]:Deviation of calibrated current ( 320channels)"908 "|I[uA]:Calibrated currents per pixel" 909 "|I_avg[uA]:Average calibrated current (N channels)" 910 "|I_rms[uA]:Rms of calibrated current (N channels)" 911 "|I_med[uA]:Median calibrated current (N channels)" 912 "|I_dev[uA]:Deviation of calibrated current (N channels)" 1539 913 "|N[uint16]:Number of valid values" 1540 "|T_diff[s]:Time difference to calibration"), 1541 fSP(BIAS::kNumChannels), 1542 fKp(0), fKi(0), fKd(0), fT(-1), 1543 fCalibrationOffset(-3), 914 "|T_diff[s]:Time difference to calibration" 915 "|U_ov[V]:Calculated overvoltage" 916 "|U_nom[V]:Nominal overvoltage" 917 "|dU_temp[V]:Correction calculated from temperature" 918 ), 1544 919 fCurrentRequestInterval(0), 1545 920 fNumCalibIgnore(30), 1546 fNumCalibRequests(300), 1547 fOutputEnabled(false) 1548 { 1549 // ba::io_service::work is a kind of keep_alive for the loop. 1550 // It prevents the io_service to go to stopped state, which 1551 // would prevent any consecutive calls to run() 1552 // or poll() to do nothing. reset() could also revoke to the 1553 // previous state but this might introduce some overhead of 1554 // deletion and creation of threads and more. 1555 921 fNumCalibRequests(300) 922 { 1556 923 fDim.Subscribe(*this); 1557 fDimFAD.Subscribe(*this);1558 924 fDimFSC.Subscribe(*this); 1559 925 fDimBias.Subscribe(*this); 926 927 fDimBias.SetCallback(bind(&StateMachineFeedback::HandleBiasStateChange, this)); 1560 928 1561 929 Subscribe("BIAS_CONTROL/CURRENT") … … 1563 931 Subscribe("BIAS_CONTROL/VOLTAGE") 1564 932 (bind(&StateMachineFeedback::HandleBiasVoltage, this, placeholders::_1)); 1565 Subscribe("BIAS_CONTROL/ FEEDBACK_DATA")1566 (bind(&StateMachineFeedback::HandleBiasDa ta,this, placeholders::_1));933 Subscribe("BIAS_CONTROL/DAC") 934 (bind(&StateMachineFeedback::HandleBiasDac, this, placeholders::_1)); 1567 935 Subscribe("BIAS_CONTROL/NOMINAL") 1568 936 (bind(&StateMachineFeedback::HandleBiasNom, this, placeholders::_1)); … … 1576 944 AddStateName(Feedback::State::kDisconnected, "Disconnected", 1577 945 "The Dim DNS is reachable, but the required subsystems are not available."); 1578 1579 946 AddStateName(Feedback::State::kConnecting, "Connecting", 1580 "Only biasctrl is available and connected with its hardware."); 1581 1582 AddStateName(Feedback::State::kConnectedFSC, "ConnectedFSC", 947 "Either biasctrl or fscctrl not connected."); 948 AddStateName(Feedback::State::kConnected, "Connected", 1583 949 "biasctrl and fscctrl are available and connected with their hardware."); 1584 AddStateName(Feedback::State::kConnectedFAD, "ConnectedFAD", 1585 "biasctrl and fadctrl are available and connected with their hardware."); 1586 AddStateName(Feedback::State::kConnected, "Connected", 1587 "biasctrl, fadctrl and fscctrl are available and connected with their hardware."); 1588 1589 AddStateName(Feedback::State::kFeedbackCtrlIdle, "FeedbackIdle", 1590 "Feedback control activated, but voltage output disabled."); 1591 AddStateName(Feedback::State::kTempCtrlIdle, "TempCtrlIdle", 1592 "Temperature control activated, but voltage output disabled."); 1593 AddStateName(Feedback::State::kCurrentCtrlIdle, "CurrentCtrlIdle", 1594 "Current control activated, but voltage output disabled."); 1595 1596 AddStateName(Feedback::State::kFeedbackCtrlRunning, "FeedbackControl", 1597 "Feedback control activated and voltage output enabled."); 1598 AddStateName(Feedback::State::kTempCtrlRunning, "TempControl", 1599 "Temperature control activated and voltage output enabled."); 1600 AddStateName(Feedback::State::kCurrentCtrlRunning, "CurrentControl", 1601 "Current/Temp control activated and voltage output enabled."); 950 1602 951 AddStateName(Feedback::State::kCalibrating, "Calibrating", 1603 "Calibrating current offsets."); 1604 1605 AddEvent("START_FEEDBACK_CONTROL", "S:1", Feedback::State::kConnectedFAD, Feedback::State::kConnected) 1606 (bind(&StateMachineFeedback::StartFeedback, this, placeholders::_1)) 1607 ("Start the feedback control loop" 1608 "|Num[short]:Number of events 'medianed' to calculate the correction value"); 1609 1610 AddEvent("START_GLOBAL_FEEDBACK", "S:1", Feedback::State::kConnectedFAD, Feedback::State::kConnected) 1611 (bind(&StateMachineFeedback::StartFeedbackGlobal, this, placeholders::_1)) 1612 ("Start the global feedback control loop" 1613 "Num[short]:Number of events averaged to calculate the correction value"); 1614 1615 AddEvent("START_TEMP_CONTROL", "F:1", Feedback::State::kConnectedFSC, Feedback::State::kConnected) 1616 (bind(&StateMachineFeedback::StartTempCtrl, this, placeholders::_1)) 1617 ("Start the temperature control loop" 1618 "|offset[V]:Offset from the nominal temperature corrected value in Volts"); 1619 1620 AddEvent("START_CURRENT_CONTROL", "F:1", Feedback::State::kConnectedFSC, Feedback::State::kConnected) 1621 (bind(&StateMachineFeedback::StartCurrentCtrl, this, placeholders::_1)) 952 "Bias crate calibrating in progress."); 953 AddStateName(Feedback::State::kCalibrated, "Calibrated", 954 "Bias crate calibrated."); 955 956 AddStateName(Feedback::State::kWaitingForData, "WaitingForData", 957 "Current control started, waiting for valid temperature and current data."); 958 AddStateName(Feedback::State::kInProgress, "InProgress", 959 "Current control in progress."); 960 961 962 /* 963 AddEvent("SET_CURRENT_REQUEST_INTERVAL") 964 (bind(&StateMachineFeedback::SetCurrentRequestInterval, this, placeholders::_1)) 965 ("|interval[ms]:Interval between two current requests in modes which need that."); 966 */ 967 968 AddEvent("CALIBRATE", Feedback::State::kConnected, Feedback::State::kCalibrated) 969 (bind(&StateMachineFeedback::Calibrate, this)) 970 (""); 971 972 AddEvent("START", "F:1", Feedback::State::kCalibrated) 973 (bind(&StateMachineFeedback::Start, this, placeholders::_1)) 1622 974 ("Start the current/temperature control loop" 1623 "|offset[V]:Offset from the nominal current/temperature corrected value in Volts"); 1624 1625 // Feedback::State::kTempCtrlIdle, Feedback::State::kFeedbackCtrlIdle, Feedback::State::kTempCtrlRunning, Feedback::State::kFeedbackCtrlRunning 975 "|Uov[V]:Overvoltage to be applied (standard value is 1.1V)"); 976 1626 977 AddEvent("STOP") 1627 978 (bind(&StateMachineFeedback::StopFeedback, this)) 1628 979 ("Stop any control loop"); 1629 980 1630 AddEvent("ENABLE_OUTPUT", "B:1")//, Feedback::State::kIdle) 1631 (bind(&StateMachineFeedback::EnableOutput, this, placeholders::_1)) 1632 ("Enable sending of correction values caluclated by the control loop to the biasctrl"); 1633 1634 AddEvent("STORE_REFERENCE")//, Feedback::State::kIdle) 1635 (bind(&StateMachineFeedback::StoreReference, this)) 1636 ("Store the last (averaged) value as new reference (for debug purpose only)"); 1637 1638 AddEvent("SET_REFERENCE", "F:1")//, Feedback::State::kIdle) 1639 (bind(&StateMachineFeedback::SetReference, this, placeholders::_1)) 1640 ("Set a new global reference value (for debug purpose only)"); 1641 1642 AddEvent("SET_Ki", "D:1")//, Feedback::State::kIdle) 1643 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 0)) 1644 ("Set integral constant Ki"); 1645 1646 AddEvent("SET_Kp", "D:1")//, Feedback::State::kIdle) 1647 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 1)) 1648 ("Set proportional constant Kp"); 1649 1650 AddEvent("SET_Kd", "D:1")//, Feedback::State::kIdle) 1651 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 2)) 1652 ("Set derivative constant Kd"); 1653 1654 AddEvent("SET_T", "D:1")//, Feedback::State::kIdle) 1655 (bind(&StateMachineFeedback::SetConstant, this, placeholders::_1, 3)) 1656 ("Set time-constant. (-1 to use the cycle time, i.e. the time for the last average cycle, instead)"); 1657 1658 AddEvent("CALIBRATE_CURRENTS", Feedback::State::kConnectedFSC, Feedback::State::kConnected)//, Feedback::State::kIdle) 1659 (bind(&StateMachineFeedback::CalibrateCurrents, this)) 1660 (""); 1661 1662 AddEvent("SET_CURRENT_REQUEST_INTERVAL", Feedback::State::kConnectedFSC, Feedback::State::kConnected)//, Feedback::State::kIdle) 1663 (bind(&StateMachineFeedback::SetCurrentRequestInterval, this, placeholders::_1)) 1664 ("|interval[ms]:Interval between two current requests in modes which need that."); 1665 1666 // Verbosity commands 1667 // AddEvent("SET_VERBOSE", "B:1") 1668 // (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1)) 1669 // ("set verbosity state" 1670 // "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); 981 982 AddEvent("ENABLE_OLD_ALRGORITHM", "B:1", Feedback::State::kConnected, Feedback::State::kCalibrated) 983 (bind(&StateMachineFeedback::EnableOldAlgorithm, this, placeholders::_1)); 984 1671 985 1672 986 AddEvent("PRINT") 1673 987 (bind(&StateMachineFeedback::Print, this)) 1674 988 (""); 1675 1676 989 AddEvent("PRINT_CALIBRATION") 1677 990 (bind(&StateMachineFeedback::PrintCalibration, this)) 1678 991 (""); 992 993 // Verbosity commands 994 AddEvent("SET_VERBOSE", "B:1") 995 (bind(&StateMachineFeedback::SetVerbosity, this, placeholders::_1)) 996 ("set verbosity state" 997 "|verbosity[bool]:disable or enable verbosity when calculating overvoltage"); 1679 998 } 1680 999 1681 1000 int EvalOptions(Configuration &conf) 1682 1001 { 1002 fIsVerbose = !conf.Get<bool>("quiet"); 1003 1683 1004 if (!fMap.Read(conf.Get<string>("pixel-map-file"))) 1684 1005 { … … 1686 1007 return 1; 1687 1008 } 1688 1689 fGain = 0.1; // V(Amplitude) / V(Bias)1690 1691 // 148 -> 2481692 1693 // 33 : 10s < 2%1694 // 50 : 5s < 2%1695 // 66 : 3s < 2%1696 // 85 : 2s < 2%1697 1698 fKp = 0;1699 fKd = 0;1700 fKi = 0.75;1701 fT = 1;1702 1703 // Is that independent of the aboslute real amplitude of1704 // the light pulser?1705 1706 ostringstream msg;1707 msg << "Control loop parameters: ";1708 msg << "Kp=" << fKp << ", Kd=" << fKd << ", Ki=" << fKi << ", ";1709 if (fT>0)1710 msg << fT;1711 else1712 msg << "<auto>";1713 msg << ", Gain(DRS/BIAS)=" << fGain << "V/V";1714 1715 Message(msg);1716 1009 1717 1010 fCurrentRequestInterval = conf.Get<uint16_t>("current-request-interval"); 1718 1011 fNumCalibIgnore = conf.Get<uint16_t>("num-calib-ignore"); 1719 1012 fNumCalibRequests = conf.Get<uint16_t>("num-calib-average"); 1720 fCalibrationOffset = conf.Get<float>("calibration-offset");1721 1013 1722 1014 return -1; … … 1738 1030 po::options_description control("Feedback options"); 1739 1031 control.add_options() 1032 ("quiet,q", po_bool(true), "Disable printing more information on average overvoltagecontents of all received messages (except dynamic data) in clear text.") 1740 1033 ("pixel-map-file", var<string>()->required(), "Pixel mapping file. Used here to get the default reference voltage.") 1741 1034 ("current-request-interval", var<uint16_t>(1000), "Interval between two current requests.") 1742 1035 ("num-calib-ignore", var<uint16_t>(30), "Number of current requests to be ignored before averaging") 1743 1036 ("num-calib-average", var<uint16_t>(300), "Number of current requests to be averaged") 1744 ("calibration-offset", var<float>(-3), "Absolute offset relative to the G-APD operation voltage when calibrating")1745 1037 ; 1746 1038
Note:
See TracChangeset
for help on using the changeset viewer.