FACT++  1.0
int StateMachineFeedback::HandleBiasCurrent ( const EventImp evt)
inlineprivate

Definition at line 457 of file feedback.cc.

References __attribute__, begin, PixelMapEntry::count(), data, Debug, EventImp::GetSize(), EventImp::GetTime(), PixelMapEntry::group(), PixelMap::hv(), i, Time::IsValid(), Feedback::State::kCalibrated, Feedback::State::kCalibrating, Feedback::State::kConnected, Feedback::State::kInProgress, BIAS::kNumChannels, Feedback::State::kOnStandby, BIAS::State::kRamping, BIAS::State::kVoltageOn, Feedback::State::kWaitingForData, EventImp::Ptr(), Dim::SendCommandNB(), DimClient::sendCommandNB(), DimDescribedService::setData(), DimService::setQuality(), DimState::state(), Time::UnixTime(), and DimDescribedService::Update().

Referenced by StateMachineFeedback().

458  {
459  if (!CheckEventSize(evt.GetSize(), "HandleBiasCurrent", BIAS::kNumChannels*sizeof(uint16_t)))
461 
463  return GetCurrentState();
464 
465  // ------------------------------- HandleCalibration -----------------------------------
467  return HandleCalibration(evt);
468 
469  // ---------------------- Calibrated, WaitingForData, InProgress -----------------------
470 
471  // We are waiting but no valid temperature yet, go on waiting
473  (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
474  return GetCurrentState();
475 
476  // We are waiting but biasctrl is still in ramping (this might
477  // be the case if the feedback was started with a new overvoltage
478  // while the last ramping command was still in progress)
481  return GetCurrentState();
482 
483  // We are already in progress but no valid temperature update anymore
485  (!fTimeTemp.IsValid() || Time()-fTimeTemp>boost::posix_time::minutes(5)))
486  {
487  Warn("Current control in progress, but last received temperature older than 5min... switching voltage off.");
488  Dim::SendCommandNB("BIAS_CONTROL/SET_ZERO_VOLTAGE");
490  }
491 
492  // ---------------------- Calibrated, WaitingForData, InProgress -----------------------
493 
494  const int Navg = fDimBias.state()!=BIAS::State::kVoltageOn ? 1 : 3;
495 
496  const vector<float> &Imes = AverageCurrents(evt.Ptr<int16_t>(), Navg).first;
497  if (Imes.size()==0)
498  return GetCurrentState();
499 
500  fCurrentsAvg.assign(BIAS::kNumChannels, 0);
501  fCurrentsRms.assign(BIAS::kNumChannels, 0);
502  fCursorCur = 0;
503 
504  // -------------------------------------------------------------------------------------
505  // Inner patches to be blocked (operated below the operation voltage) in moon mode
506 
507  static const array<int, 14> inner0 =
508  {{
509  62, 63, 130, 131, 132, 133, 134,
510  135, 222, 223, 292, 293, 294, 295,
511  }};
512 
513  static const array<int, 23> inner1 =
514  {{
515  58, 59, 60, 61, 129, 138, 139, 140, 141, 142, 143, 218,
516  219, 220, 221, 290, 291, 298, 299, 300, 301, 302, 303,
517  }};
518 
519  static const array<int, 43> inner2 =
520  {{
521  42, 43, 44, 45, 55, 56, 57, 70, 71, 78, 79,
522  96, 97, 98, 99, 102, 103, 128, 136, 137, 159, 202,
523  203, 204, 205, 214, 216, 217, 228, 230, 231, 256, 257,
524  258, 259, 262, 263, 288, 289, 296, 297, 310, 318
525  }};
526 
527  // -------------------------------------------------------------------------------------
528 
529  // Nominal overvoltage (w.r.t. the bias setup values)
530  const double voltageoffset = GetCurrentState()<Feedback::State::kWaitingForData ? 0 : fUserOffset;
531 
532  double avg[2] = { 0, 0 };
533  double min[2] = { 90, 90 };
534  double max[2] = { -90, -90 };
535  int num[3] = { 0, 0, 0 };
536 
537  vector<double> med[3];
538  med[0].resize(BIAS::kNumChannels);
539  med[1].resize(BIAS::kNumChannels);
540  med[2].resize(BIAS::kNumChannels);
541 
542  struct dim_data
543  {
544  float I[BIAS::kNumChannels];
545  float Iavg;
546  float Irms;
547  float Imed;
548  float Idev;
549  uint32_t N;
550  float Tdiff;
551  float Uov[BIAS::kNumChannels];
552  float Unom;
553  float dUtemp;
554 
555  dim_data() { memset(this, 0, sizeof(dim_data)); }
556  } __attribute__((__packed__));
557 
558  int Ndev[3] = { 0, 0, 0 };
559 
560  dim_data data;
561 
562  data.Unom = voltageoffset;
563  data.dUtemp = fTempOffsetAvg;
564 
565  vector<float> vec(BIAS::kNumChannels);
566 
567  // ================================= old =======================
568  // Pixel 583: 5 31 == 191 (5) C2 B3 P3
569  // Pixel 830: 2 2 == 66 (4) C0 B8 P1
570  // Pixel 1401: 6 1 == 193 (5) C2 B4 P0
571 
572  double UdrpAvg = 0;
573  double UdrpRms = 0;
574 
575  for (int i=0; i<320/*BIAS::kNumChannels*/; i++)
576  {
577  const PixelMapEntry &hv = fMap.hv(i);
578  if (!hv)
579  continue;
580 
581  // Check if this is a blocked channel
582  // 272 is the one with the shortcut
583  const bool blocked =
584  (fMoonMode>0 && std::find(inner0.begin(), inner0.end(), i)!=inner0.end()) ||
585  (fMoonMode>1 && std::find(inner1.begin(), inner1.end(), i)!=inner1.end()) ||
586  (fMoonMode>2 && std::find(inner2.begin(), inner2.end(), i)!=inner2.end()) ||
587  i==272;
588 
589  // Number of G-APDs in this patch
590  const int N = hv.count();
591 
592  // Average measured ADC value for this channel
593  // FIXME: This is a workaround for the problem with the
594  // readout of bias voltage channel 263
595  const double adc = Imes[i]/* * (5e-3/4096)*/; // [A]
596 
597  // Current through ~100 Ohm measurement resistor
598  //const double I8 = (adc-fCalibDeltaI[i])*fCalibR8[i]/100;
599  const double I8 = adc-fCalibDeltaI[i];
600 
601  // Current through calibration resistors (R9)
602  // This is uncalibrated, but since the corresponding calibrated
603  // value I8 is subtracted, the difference should yield a correct value
604  const double I9 = fBiasDac[i] * (1e-3/4096);//U9/R9; [A]
605 
606  // Current in R4/R5 branch
607  //const double Iout = I8 - I9;//I8>I9 ? I8 - I9 : 0;
608  const double Iout = I8 - I9*100/fCalibR8[i];//I8>I9 ? I8 - I9 : 0;
609 
610  // Applied voltage at calibration resistors, according to biasctrl
611  const double U9 = fBiasVolt[i];
612 
613  // new I8 - I9*100/fCalibR8 100
614  // change = --- = ---------------------- = -------- = 0.8
615  // old I8*fCalibR8/100 - I9 fCalibR8
616 
617  // Serial resistors (one 1kOhm at the output of the bias crate, one 1kOhm in the camera)
618  const double R4 = 2000;
619 
620  // Serial resistor of the individual G-APDs plus 50 Ohm termination
621  double R5 = 3900./N + 50;
622 
623  // This is assuming that the broken pixels have a 390 Ohm instead of 3900 Ohm serial resistor
624  if (i==66 || i==193) // Pixel 830(66) / Pixel 583(191)
625  R5 = 1./((N-1)/3900.+1/1000.);
626  if (i==191) // Pixel 1399(193)
627  R5 = 1./((N-1)/3900.+1/390.);
628  if (i==17 || i==206) // dead pixel 923(80) / dead pixel 424(927)
629  R5 = 3900./(N-1); // cannot identify third dead pixel in light-pulser data
630 
631  // The measurement resistor
632  const double R8 = 0;
633 
634  // Total resistance of branch with diodes (R4+R5)
635  // Assuming that the voltage output of the OpAMP is linear
636  // with the DAC setting and not the voltage at R9, the
637  // additional voltage drop at R8 must be taken into account
638  const double R = R4 + R5 + R8;
639 
640  // For the patches with a broken resistor - ignoring the G-APD resistance -
641  // we get:
642  //
643  // I[R=3900] = Iout * 1/(10+(N-1)) = Iout /(N+9)
644  // I[R= 390] = Iout * (1 - 1/(10+(N-1))) = Iout * (N+8)/(N+9)
645  //
646  // I[R=390] / I[R=3900] = N+8
647  //
648  // Udrp = Iout*3900/(N+9) + Iout*1000 + Iout*1000 = Iout * R
649 
650  // Voltage drop in R4/R5 branch (for the G-APDs with correct resistor)
651  // The voltage drop should not be <0, otherwise an unphysical value
652  // would be amplified when Uset is calculated.
653  const double Udrp = Iout<0 ? 0 : R*Iout;
654 
655  // Nominal operation voltage with correction for temperature dependence
656  const double Uop = fVoltGapd[i] + fVoltOffset[i] + fTempOffset[i]
657  + (blocked ? -5 : 0);
658 
659  // Current overvoltage (at a G-APD with the correct 3900 Ohm resistor)
660  // expressed w.r.t. to the operation voltage
661  const double Uov = (U9-Udrp)-Uop>-1.4 ? (U9-Udrp)-Uop : -1.4;
662 
663  // The current through one G-APD is the sum divided by the number of G-APDs
664  // (assuming identical serial resistors)
665  double Iapd = Iout/N;
666 
667  // Rtot = Uapd/Iout
668  // Ich = Uapd/Rch = (Rtot*Iout) / Rch = Rtot/Rch * Iout
669  //
670  // Rtot = 3900/N
671  // Rch = 3900
672  //
673  // Rtot = 1./((N-1)/3900 + 1/X) X=390 or X=1000
674  // Rch = 3900
675  //
676  // Rtot/Rch = 1/((N-1)/3900 + 1/X)/3900
677  // Rtot/Rch = 1/( [ X*(N-1) + 3900 ] / [ 3900 * X ])/3900
678  // Rtot/Rch = X/( [ X*(N-1)/3900 + 1 ] )/3900
679  // Rtot/Rch = X/( [ X*(N-1) + 3900 ] )
680  // Rtot/Rch = 1/( [ (N-1) + 3900/X ] )
681  //
682  // Rtot/Rch[390Ohm] = 1/( [ N + 9.0 ] )
683  // Rtot/Rch[1000Ohm] = 1/( [ N + 2.9 ] )
684  //
685  // In this and the previosu case we neglect the resistance of the G-APDs, but we can make an
686  // assumption: The differential resistance depends more on the NSB than on the PDE,
687  // thus it is at least comparable for all G-APDs in the patch. In addition, although the
688  // G-APD with the 390Ohm serial resistor has the wrong voltage applied, this does not
689  // significantly influences the ohmic resistor or the G-APD because the differential
690  // resistor is large enough that the increase of the overvoltage does not dramatically
691  // increase the current flow as compared to the total current flow.
692  if (i==66 || i==193) // Iout/13 15.8 / Iout/14 16.8
693  Iapd = Iout/(N+2.9);
694  if (i==191) // Iout/7.9 38.3
695  Iapd = Iout/(N+9);
696  if (i==17 || i==206)
697  Iapd = Iout/(N-1);
698 
699  // The differential resistance of the G-APD, i.e. the dependence of the
700  // current above the breakdown voltage, is given by
701  //const double Rapd = Uov/Iapd;
702  // This allows us to estimate the current Iov at the overvoltage we want to apply
703  //const double Iov = overvoltage/Rapd;
704 
705  // Estimate set point for over-voltage (voltage drop at the target point)
706  // This estimation is based on the linear increase of the
707  // gain with voltage and the increase of the crosstalk with
708  // voltage, as measured with the overvoltage-tests (OVTEST)
709  /*
710  Uov+0.44<0.022 ?
711  Ubd + overvoltage + Udrp*exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44), 0.6) :
712  Ubd + overvoltage + Udrp*exp(0.6*(overvoltage-Uov))*pow((overvoltage+0.44)/(Uov+0.44), 0.6);
713  */
714  const double Uset =
715  Uov+1.4<0.022 ?
716  Uop + voltageoffset + Udrp*exp(0.6*(voltageoffset-Uov))*pow((voltageoffset+1.4), 0.6) :
717  Uop + voltageoffset + Udrp*exp(0.6*(voltageoffset-Uov))*pow((voltageoffset+1.4)/(Uov+1.4), 0.6);
718 
719  if (fabs(voltageoffset-Uov)>0.033)
720  Ndev[0]++;
721  if (fabs(voltageoffset-Uov)>0.022)
722  Ndev[1]++;
723  if (fabs(voltageoffset-Uov)>0.011)
724  Ndev[2]++;
725 
726  // Voltage set point
727  vec[i] = Uset;
728 
729  const double iapd = Iapd*1e6; // A --> uA
730 
731  data.I[i] = iapd;
732  data.Uov[i] = Uov;
733 
734  if (!blocked)
735  {
736  const int g = hv.group();
737 
738  med[g][num[g]] = Uov;
739  avg[g] += Uov;
740  num[g]++;
741 
742  if (Uov<min[g])
743  min[g] = Uov;
744  if (Uov>max[g])
745  max[g] = Uov;
746 
747  data.Iavg += iapd;
748  data.Irms += iapd*iapd;
749 
750  med[2][num[2]++] = iapd;
751 
752  UdrpAvg += Udrp;
753  UdrpRms += Udrp*Udrp;
754  }
755  }
756 
757 
758  // ---------------------------- Calculate statistics ----------------------------------
759 
760  // average and rms
761  data.Iavg /= num[2];
762  data.Irms /= num[2];
763  data.Irms -= data.Iavg*data.Iavg;
764 
765  data.N = num[2];
766  data.Irms = data.Irms<0 ? 0: sqrt(data.Irms);
767 
768  // median
769  sort(med[2].data(), med[2].data()+num[2]);
770 
771  data.Imed = num[2]%2 ? med[2][num[2]/2] : (med[2][num[2]/2-1]+med[2][num[2]/2])/2;
772 
773  // deviation
774  for (int i=0; i<num[2]; i++)
775  med[2][i] = fabs(med[2][i]-data.Imed);
776 
777  sort(med[2].data(), med[2].data()+num[2]);
778 
779  data.Idev = med[2][uint32_t(0.682689477208650697*num[2])];
780 
781  // time difference to calibration
782  data.Tdiff = evt.GetTime().UnixTime()-fTimeCalib.UnixTime();
783 
784  // Average overvoltage
785  const double Uov = (avg[0]+avg[1])/(num[0]+num[1]);
786 
787  // ------------------------------- Update voltages ------------------------------------
788 
789  int newstate = GetCurrentState();
790 
791  if (GetCurrentState()!=Feedback::State::kCalibrated) // WaitingForData, OnStandby, InProgress, kWarning, kCritical
792  {
794  {
795  newstate = CheckLimits(data.I);
796 
797  // standby and change reduction level of voltage
798  if (newstate==Feedback::State::kOnStandby)
799  {
800  // Calculate average applied overvoltage and estimate an offset
801  // to reach fAbsoluteMedianCurrentLimit
802  float fAbsoluteMedianCurrentLimit = 85;
803  const double deltaU = (Uov+1.4)*(1-pow(fAbsoluteMedianCurrentLimit/data.Imed, 1./1.7));
804 
805  if (fVoltageReduction+deltaU<0.033)
806  fVoltageReduction = 0;
807  else
808  {
809  fVoltageReduction += deltaU;
810 
811  for (int i=0; i<320; i++)
812  vec[i] -= fVoltageReduction;
813  }
814  }
815 
816  // FIXME: What if the brightest pixel gets too bright???
817  // FIXME: What if fVolatgeReduction > U1.4V?
818 
819  // set voltage in 262 -> current in 262/263
820  vec[263] = vec[262]-fVoltGapd[262]+fVoltGapd[263];
821 
822  // Do not ramp the channel with a shortcut
823  vec[272] = 0;
824 
825 // if (fDimBias.state()!=BIAS::State::kRamping)
826 // {
827  DimClient::sendCommandNB("BIAS_CONTROL/SET_ALL_CHANNELS_VOLTAGE",
828  vec.data(), BIAS::kNumChannels*sizeof(float));
829 
830  UdrpAvg /= 320;
831  UdrpRms /= 320;
832  UdrpRms -= UdrpAvg*UdrpAvg;
833  UdrpRms = UdrpRms<0 ? 0 : sqrt(UdrpRms);
834 
835  ostringstream msg;
836  msg << fixed;
837  msg << setprecision(2) << "dU(" << fTemp << "degC)="
838  << setprecision(3) << fTempOffsetAvg << "V+-" << fTempOffsetRms << " Udrp="
839  << UdrpAvg << "V+-" << UdrpRms;
840  msg.unsetf(ios_base::floatfield);
841 
842  if (fVoltageReduction==0)
843  msg << " Unom=" << voltageoffset << "V";
844  else
845  msg << " Ured=" << fVoltageReduction << "V";
846 
847  msg << " Uov=" << Uov;
848  msg << " Imed=" << data.Imed << "uA [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
849  Info(msg);
850  }
851  }
852  else
853  {
855  {
856  ostringstream msg;
857  msg << setprecision(4) << "Current status: dU(" << fTemp << "degC)=" << fTempOffsetAvg << "V+-" << fTempOffsetRms << ", Unom=" << voltageoffset << "V, Uov=" << (num[0]+num[1]>0?(avg[0]+avg[1])/(num[0]+num[1]):0) << " [N=" << Ndev[0] << "/" << Ndev[1] << "/" << Ndev[2] << "]";
858  Info(msg);
859  }
860  }
861 
862  //if (GetCurrentState()>=Feedback::State::kOnStandby &&
863  // fDimBias.state()==BIAS::State::kRamping)
864  // return newstate;
865 
866  // --------------------------------- Console out --------------------------------------
867 
869  {
870  sort(med[0].begin(), med[0].begin()+num[0]);
871  sort(med[1].begin(), med[1].begin()+num[1]);
872 
873  ostringstream msg;
874  msg << " Avg0=" << setw(7) << avg[0]/num[0] << " | Avg1=" << setw(7) << avg[1]/num[1];
875  Debug(msg);
876 
877  msg.str("");
878  msg << " Med0=" << setw(7) << med[0][num[0]/2] << " | Med1=" << setw(7) << med[1][num[1]/2];
879  Debug(msg);
880 
881  msg.str("");
882  msg << " Min0=" << setw(7) << min[0] << " | Min1=" << setw(7) << min[1];
883  Debug(msg);
884 
885  msg.str("");
886  msg << " Max0=" << setw(7) << max[0] << " | Max1=" << setw(7) << max[1];
887  Debug(msg);
888  }
889 
890  // ---------------------------- Calibrated Currents -----------------------------------
891 
892  // FIXME:
893  // + Current overvoltage
894  // + Temp offset
895  // + User offset
896  // + Command overvoltage
898  fDimCurrents.setData(&data, sizeof(dim_data));
899  fDimCurrents.Update(evt.GetTime());
900 
901  // FIXME: To be checked
903  }
DimDescribedState fDimBias
Definition: feedback.cc:38
int CheckLimits(const float *I)
Definition: feedback.cc:350
int GetCurrentState() const
return the current state of the machine
bool CheckEventSize(size_t has, const char *name, size_t size)
Definition: feedback.cc:103
int HandleCalibration(const EventImp &evt)
Definition: feedback.cc:203
int Debug(const std::string &str)
Definition: MessageImp.h:45
int i
Definition: db_dim_client.c:21
void setQuality(int quality)
Definition: discpp.cxx:1256
int group() const
Definition: PixelMap.h:40
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
vector< float > fCalibR8
Definition: feedback.cc:59
double begin
const int32_t & state() const
Definition: DimState.h:80
vector< double > fTempOffset
Definition: feedback.cc:69
vector< int64_t > fCurrentsAvg
Definition: feedback.cc:49
vector< float > fCalibDeltaI
Definition: feedback.cc:58
int count() const
Definition: PixelMap.h:41
pair< vector< float >, vector< float > > AverageCurrents(const int16_t *ptr, int n)
Definition: feedback.cc:174
vector< float > fVoltGapd
Definition: feedback.cc:52
vector< uint16_t > fBiasDac
Definition: feedback.cc:55
void setData(const void *ptr, size_t sz)
double UnixTime() const
Definition: Time.cc:195
virtual Time GetTime() const
Definition: EventImp.h:57
static void sendCommandNB(const char *name, int data)
Definition: diccpp.cxx:1140
DimDescribedService fDimCurrents
Definition: feedback.cc:43
vector< int64_t > fCurrentsRms
Definition: feedback.cc:50
typedef __attribute__
void SendCommandNB(const std::string &command)
Definition: Dim.h:30
bool IsValid() const
Definition: Time.h:90
int Warn(const std::string &str)
Definition: MessageImp.h:48
vector< double > fVoltOffset
Definition: feedback.cc:75
float data[4 *1440]
double fVoltageReduction
Definition: feedback.cc:68
int Info(const std::string &str)
Definition: MessageImp.h:47
uint16_t fMoonMode
Definition: feedback.cc:77
vector< float > fBiasVolt
Definition: feedback.cc:53
const T * Ptr(size_t offset=0) const
Definition: EventImp.h:74
const PixelMapEntry & hv(int board, int channel) const
Definition: PixelMap.h:139
virtual size_t GetSize() const
Definition: EventImp.h:55

+ Here is the call graph for this function:

+ Here is the caller graph for this function: