File indexing completed on 2024-05-23 03:13:04
0001 #include "DataFormats/Math/interface/deltaPhi.h"
0002 #include "DataFormats/Math/interface/libminifloat.h"
0003 #include "DataFormats/PatCandidates/interface/PackedCandidate.h"
0004 #include "DataFormats/SiPixelDetId/interface/PixelSubdetector.h"
0005 #include "DataFormats/SiStripDetId/interface/StripSubdetector.h"
0006
0007 #include "DataFormats/Math/interface/liblogintpack.h"
0008 #include "FWCore/Utilities/interface/isFinite.h"
0009
0010 #include "TMatrixDSym.h"
0011 #include "TVectorD.h"
0012 using namespace logintpack;
0013
0014 CovarianceParameterization pat::PackedCandidate::covarianceParameterization_;
0015 std::once_flag pat::PackedCandidate::covariance_load_flag;
0016
0017 void pat::PackedCandidate::pack(bool unpackAfterwards) {
0018 float unpackedPt = std::min<float>(p4_.load()->Pt(), MiniFloatConverter::max());
0019 packedPt_ = MiniFloatConverter::float32to16(unpackedPt);
0020 packedEta_ = int16_t(std::round(p4_.load()->Eta() / 6.0f * std::numeric_limits<int16_t>::max()));
0021 packedPhi_ = int16_t(std::round(p4_.load()->Phi() / 3.2f * std::numeric_limits<int16_t>::max()));
0022 packedM_ = MiniFloatConverter::float32to16(p4_.load()->M());
0023 if (unpackAfterwards) {
0024 delete p4_.exchange(nullptr);
0025 delete p4c_.exchange(nullptr);
0026 unpack();
0027 }
0028 }
0029
0030 void pat::PackedCandidate::packVtx(bool unpackAfterwards) {
0031 reco::VertexRef pvRef = vertexRef();
0032 Point pv = pvRef.isNonnull() ? pvRef->position() : Point();
0033 float dxPV = vertex_.load()->X() - pv.X(),
0034 dyPV = vertex_.load()->Y() - pv.Y();
0035 float s = std::sin(float(p4_.load()->Phi()) + dphi_),
0036 c = std::cos(float(p4_.load()->Phi() + dphi_));
0037
0038 dxy_ = -dxPV * s + dyPV * c;
0039
0040
0041
0042 dz_ = 0;
0043 if (p4_.load()->Pt() != 0.f) {
0044 float pzpt = p4_.load()->Pz() / p4_.load()->Pt();
0045 dz_ = vertex_.load()->Z() - pv.Z() - (dxPV * c + dyPV * s) * pzpt;
0046 }
0047 packedDxy_ = MiniFloatConverter::float32to16(dxy_ * 100);
0048 packedDz_ = pvRef.isNonnull() ? MiniFloatConverter::float32to16(dz_ * 100)
0049 : int16_t(std::round(dz_ / 40.f * std::numeric_limits<int16_t>::max()));
0050 packedDPhi_ = int16_t(std::round(dphi_ / 3.2f * std::numeric_limits<int16_t>::max()));
0051 packedDEta_ = MiniFloatConverter::float32to16(deta_);
0052 packedDTrkPt_ = MiniFloatConverter::float32to16(dtrkpt_);
0053
0054 if (unpackAfterwards) {
0055 delete vertex_.exchange(nullptr);
0056 unpackVtx();
0057 }
0058 }
0059
0060 void pat::PackedCandidate::unpack() const {
0061 float pt = MiniFloatConverter::float16to32(packedPt_);
0062 double shift = (pt < 1. ? 0.1 * pt : 0.1 / pt);
0063
0064 double sign = ((int(pt * 10) % 2 == 0) ? 1 : -1);
0065 double phi = int16_t(packedPhi_) * 3.2f / std::numeric_limits<int16_t>::max() +
0066 sign * shift * 3.2 / std::numeric_limits<int16_t>::max();
0067 auto p4 = std::make_unique<PolarLorentzVector>(pt,
0068 int16_t(packedEta_) * 6.0f / std::numeric_limits<int16_t>::max(),
0069 phi,
0070 MiniFloatConverter::float16to32(packedM_));
0071 auto p4c = std::make_unique<LorentzVector>(*p4);
0072 PolarLorentzVector *expectp4 = nullptr;
0073 if (p4_.compare_exchange_strong(expectp4, p4.get())) {
0074 p4.release();
0075 }
0076
0077
0078
0079 LorentzVector *expectp4c = nullptr;
0080 if (p4c_.compare_exchange_strong(expectp4c, p4c.get())) {
0081 p4c.release();
0082 }
0083 }
0084
0085 void pat::PackedCandidate::packCovariance(const reco::TrackBase::CovarianceMatrix &m, bool unpackAfterwards) {
0086 packedCovariance_.dptdpt = packCovarianceElement(m, 0, 0);
0087 packedCovariance_.detadeta = packCovarianceElement(m, 1, 1);
0088 packedCovariance_.dphidphi = packCovarianceElement(m, 2, 2);
0089 packedCovariance_.dxydxy = packCovarianceElement(m, 3, 3);
0090 packedCovariance_.dzdz = packCovarianceElement(m, 4, 4);
0091 packedCovariance_.dxydz = packCovarianceElement(m, 3, 4);
0092 packedCovariance_.dlambdadz = packCovarianceElement(m, 1, 4);
0093 packedCovariance_.dphidxy = packCovarianceElement(m, 2, 3);
0094
0095 if (unpackAfterwards)
0096 unpackCovariance();
0097 }
0098
0099 void pat::PackedCandidate::unpackCovariance() const {
0100 const CovarianceParameterization &p = covarianceParameterization();
0101 if (p.isValid()) {
0102 auto m = std::make_unique<reco::TrackBase::CovarianceMatrix>();
0103 for (int i = 0; i < 5; i++)
0104 for (int j = 0; j < 5; j++) {
0105 (*m)(i, j) = 0;
0106 }
0107 unpackCovarianceElement(*m, packedCovariance_.dptdpt, 0, 0);
0108 unpackCovarianceElement(*m, packedCovariance_.detadeta, 1, 1);
0109 unpackCovarianceElement(*m, packedCovariance_.dphidphi, 2, 2);
0110 unpackCovarianceElement(*m, packedCovariance_.dxydxy, 3, 3);
0111 unpackCovarianceElement(*m, packedCovariance_.dzdz, 4, 4);
0112 unpackCovarianceElement(*m, packedCovariance_.dxydz, 3, 4);
0113 unpackCovarianceElement(*m, packedCovariance_.dlambdadz, 1, 4);
0114 unpackCovarianceElement(*m, packedCovariance_.dphidxy, 2, 3);
0115
0116 reco::TrackBase::CovarianceMatrix *expected = nullptr;
0117 if (m_.compare_exchange_strong(expected, m.get())) {
0118 m.release();
0119 }
0120
0121 } else {
0122 throw edm::Exception(edm::errors::UnimplementedFeature)
0123 << "You do not have a valid track parameters file loaded. "
0124 << "Please check that the release version is compatible with your "
0125 "input data"
0126 << "or avoid accessing track parameter uncertainties. ";
0127 }
0128 }
0129
0130 void pat::PackedCandidate::unpackVtx() const {
0131 reco::VertexRef pvRef = vertexRef();
0132 dphi_ = int16_t(packedDPhi_) * 3.2f / std::numeric_limits<int16_t>::max(),
0133 deta_ = MiniFloatConverter::float16to32(packedDEta_);
0134 dtrkpt_ = MiniFloatConverter::float16to32(packedDTrkPt_);
0135 dxy_ = MiniFloatConverter::float16to32(packedDxy_) / 100.;
0136 dz_ = pvRef.isNonnull() ? MiniFloatConverter::float16to32(packedDz_) / 100.
0137 : int16_t(packedDz_) * 40.f / std::numeric_limits<int16_t>::max();
0138 Point pv = pvRef.isNonnull() ? pvRef->position() : Point();
0139 float phi = p4_.load()->Phi() + dphi_, s = std::sin(phi), c = std::cos(phi);
0140 auto vertex = std::make_unique<Point>(pv.X() - dxy_ * s,
0141 pv.Y() + dxy_ * c,
0142 pv.Z() + dz_);
0143
0144
0145 Point *expected = nullptr;
0146 if (vertex_.compare_exchange_strong(expected, vertex.get())) {
0147 vertex.release();
0148 }
0149 }
0150
0151 pat::PackedCandidate::~PackedCandidate() {
0152 delete p4_.load();
0153 delete p4c_.load();
0154 delete vertex_.load();
0155 delete track_.load();
0156 delete m_.load();
0157 }
0158
0159 float pat::PackedCandidate::dxy(const Point &p) const {
0160 maybeUnpackBoth();
0161 const float phi = float(p4_.load()->Phi()) + dphi_;
0162 return -(vertex_.load()->X() - p.X()) * std::sin(phi) + (vertex_.load()->Y() - p.Y()) * std::cos(phi);
0163 }
0164 float pat::PackedCandidate::dz(const Point &p) const {
0165 maybeUnpackBoth();
0166 const float phi = float(p4_.load()->Phi()) + dphi_;
0167 const float pzpt = deta_ ? std::sinh(etaAtVtx()) : p4_.load()->Pz() / p4_.load()->Pt();
0168 return (vertex_.load()->Z() - p.Z()) -
0169 ((vertex_.load()->X() - p.X()) * std::cos(phi) + (vertex_.load()->Y() - p.Y()) * std::sin(phi)) * pzpt;
0170 }
0171
0172 const reco::Track pat::PackedCandidate::pseudoPosDefTrack() const {
0173
0174 if (!track_)
0175 unpackTrk();
0176
0177
0178 double det = 0;
0179 bool notPosDef = (!(*m_).Sub<AlgebraicSymMatrix22>(0, 0).Det(det) || det < 0) ||
0180 (!(*m_).Sub<AlgebraicSymMatrix33>(0, 0).Det(det) || det < 0) ||
0181 (!(*m_).Sub<AlgebraicSymMatrix44>(0, 0).Det(det) || det < 0) || (!(*m_).Det(det) || det < 0);
0182
0183 if (notPosDef) {
0184 reco::TrackBase::CovarianceMatrix m(*m_);
0185
0186 TMatrixDSym eigenCov(5);
0187 for (int i = 0; i < 5; i++) {
0188 for (int j = 0; j < 5; j++) {
0189 if (edm::isNotFinite((m)(i, j)))
0190 eigenCov(i, j) = 1e-6;
0191 else
0192 eigenCov(i, j) = (m)(i, j);
0193 }
0194 }
0195 TVectorD eigenValues(5);
0196 eigenCov.EigenVectors(eigenValues);
0197 double minEigenValue = eigenValues.Min();
0198 double delta = 1e-6;
0199 if (minEigenValue < 0) {
0200 for (int i = 0; i < 5; i++)
0201 m(i, i) += delta - minEigenValue;
0202 }
0203
0204
0205 return reco::Track(normalizedChi2_ * (*track_).ndof(),
0206 (*track_).ndof(),
0207 *vertex_,
0208 (*track_).momentum(),
0209 (*track_).charge(),
0210 m,
0211 reco::TrackBase::undefAlgorithm,
0212 reco::TrackBase::loose);
0213 } else {
0214
0215 return reco::Track(*track_);
0216 }
0217 }
0218
0219 void pat::PackedCandidate::unpackTrk() const {
0220 maybeUnpackBoth();
0221 math::RhoEtaPhiVector p3(ptTrk(), etaAtVtx(), phiAtVtx());
0222 maybeUnpackCovariance();
0223 int numberOfStripLayers = stripLayersWithMeasurement(), numberOfPixelLayers = pixelLayersWithMeasurement();
0224 int numberOfPixelHits = this->numberOfPixelHits();
0225 int numberOfHits = this->numberOfHits();
0226
0227 int ndof = numberOfHits + numberOfPixelHits - 5;
0228 LostInnerHits innerLost = lostInnerHits();
0229
0230 auto track = std::make_unique<reco::Track>(normalizedChi2_ * ndof,
0231 ndof,
0232 *vertex_,
0233 math::XYZVector(p3.x(), p3.y(), p3.z()),
0234 charge(),
0235 *(m_.load()),
0236 reco::TrackBase::undefAlgorithm,
0237 reco::TrackBase::loose);
0238 int i = 0;
0239 if (firstHit_ == 0) {
0240 if (innerLost == validHitInFirstPixelBarrelLayer) {
0241 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel, 1, 0, TrackingRecHit::valid);
0242 i = 1;
0243 }
0244 } else {
0245 track->appendHitPattern(firstHit_, TrackingRecHit::valid);
0246 }
0247
0248 if (firstHit_ != 0 && reco::HitPattern::pixelHitFilter(firstHit_))
0249 i = 1;
0250
0251
0252 if (innerLost == validHitInFirstPixelBarrelLayer) {
0253
0254
0255 for (; i < numberOfPixelLayers; i++) {
0256 if (i <= 3) {
0257 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel, i + 1, 0, TrackingRecHit::valid);
0258 } else {
0259 track->appendTrackerHitPattern(PixelSubdetector::PixelEndcap, i - 3, 0, TrackingRecHit::valid);
0260 }
0261 }
0262 } else {
0263
0264
0265 int iOffset = 0;
0266 if (firstHit_ != 0 && reco::HitPattern::pixelHitFilter(firstHit_)) {
0267 iOffset = reco::HitPattern::getLayer(firstHit_);
0268 if (reco::HitPattern::getSubStructure(firstHit_) == PixelSubdetector::PixelEndcap)
0269 iOffset += 3;
0270 } else {
0271 iOffset = 1;
0272 }
0273 for (; i < numberOfPixelLayers; i++) {
0274 if (i + iOffset <= 2) {
0275 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel, i + iOffset + 1, 0, TrackingRecHit::valid);
0276 } else {
0277 track->appendTrackerHitPattern(PixelSubdetector::PixelEndcap, i + iOffset - 3 + 1, 0, TrackingRecHit::valid);
0278 }
0279 }
0280 }
0281
0282
0283 for (; i < numberOfPixelHits; i++) {
0284 if (firstHit_ != 0 && reco::HitPattern::pixelHitFilter(firstHit_)) {
0285 track->appendTrackerHitPattern(reco::HitPattern::getSubStructure(firstHit_),
0286 reco::HitPattern::getLayer(firstHit_),
0287 0,
0288 TrackingRecHit::valid);
0289 } else {
0290 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel,
0291 (innerLost == validHitInFirstPixelBarrelLayer ? 1 : 2),
0292 0,
0293 TrackingRecHit::valid);
0294 }
0295 }
0296
0297
0298
0299
0300
0301 if (firstHit_ != 0 && reco::HitPattern::stripHitFilter(firstHit_))
0302 i += 1;
0303 int slOffset = 0;
0304 if (firstHit_ != 0 && reco::HitPattern::stripHitFilter(firstHit_)) {
0305 slOffset = reco::HitPattern::getLayer(firstHit_) - 1;
0306 if (reco::HitPattern::getSubStructure(firstHit_) == StripSubdetector::TID)
0307 slOffset += 4;
0308 if (reco::HitPattern::getSubStructure(firstHit_) == StripSubdetector::TOB)
0309 slOffset += 7;
0310 if (reco::HitPattern::getSubStructure(firstHit_) == StripSubdetector::TEC)
0311 slOffset += 13;
0312 }
0313 for (int sl = slOffset; sl < numberOfStripLayers + slOffset; ++sl, ++i) {
0314 if (sl < 4)
0315 track->appendTrackerHitPattern(StripSubdetector::TIB, sl + 1, 1, TrackingRecHit::valid);
0316 else if (sl < 4 + 3)
0317 track->appendTrackerHitPattern(StripSubdetector::TID, (sl - 4) + 1, 1, TrackingRecHit::valid);
0318 else if (sl < 7 + 6)
0319 track->appendTrackerHitPattern(StripSubdetector::TOB, (sl - 7) + 1, 1, TrackingRecHit::valid);
0320 else if (sl < 13 + 9)
0321 track->appendTrackerHitPattern(StripSubdetector::TEC, (sl - 13) + 1, 1, TrackingRecHit::valid);
0322 else
0323 break;
0324 }
0325
0326
0327
0328 for (; i < numberOfHits; i++) {
0329 if (reco::HitPattern::stripHitFilter(firstHit_)) {
0330 track->appendTrackerHitPattern(reco::HitPattern::getSubStructure(firstHit_),
0331 reco::HitPattern::getLayer(firstHit_),
0332 1,
0333 TrackingRecHit::valid);
0334 } else {
0335 track->appendTrackerHitPattern(StripSubdetector::TIB, 1, 1, TrackingRecHit::valid);
0336 }
0337 }
0338
0339 switch (innerLost) {
0340 case validHitInFirstPixelBarrelLayer:
0341 break;
0342 case noLostInnerHits:
0343 break;
0344 case oneLostInnerHit:
0345 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel, 1, 0, TrackingRecHit::missing_inner);
0346 break;
0347 case moreLostInnerHits:
0348 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel, 1, 0, TrackingRecHit::missing_inner);
0349 track->appendTrackerHitPattern(PixelSubdetector::PixelBarrel, 2, 0, TrackingRecHit::missing_inner);
0350 break;
0351 };
0352
0353 if (trackHighPurity())
0354 track->setQuality(reco::TrackBase::highPurity);
0355
0356 reco::Track *expected = nullptr;
0357 if (track_.compare_exchange_strong(expected, track.get())) {
0358 track.release();
0359 }
0360 }
0361
0362
0363
0364 const reco::CandidateBaseRef &pat::PackedCandidate::masterClone() const {
0365 throw cms::Exception("Invalid Reference") << "this Candidate has no master clone reference."
0366 << "Can't call masterClone() method.\n";
0367 }
0368
0369 bool pat::PackedCandidate::hasMasterClone() const { return false; }
0370
0371 bool pat::PackedCandidate::hasMasterClonePtr() const { return false; }
0372
0373 const reco::CandidatePtr &pat::PackedCandidate::masterClonePtr() const {
0374 throw cms::Exception("Invalid Reference") << "this Candidate has no master clone ptr."
0375 << "Can't call masterClonePtr() method.\n";
0376 }
0377
0378 size_t pat::PackedCandidate::numberOfDaughters() const { return 0; }
0379
0380 size_t pat::PackedCandidate::numberOfMothers() const { return 0; }
0381
0382 bool pat::PackedCandidate::overlap(const reco::Candidate &o) const {
0383 return p4() == o.p4() && vertex() == o.vertex() && charge() == o.charge();
0384
0385 }
0386
0387 const reco::Candidate *pat::PackedCandidate::daughter(size_type) const { return nullptr; }
0388
0389 const reco::Candidate *pat::PackedCandidate::mother(size_type) const { return nullptr; }
0390
0391 const reco::Candidate *pat::PackedCandidate::daughter(const std::string &) const {
0392 throw edm::Exception(edm::errors::UnimplementedFeature)
0393 << "This Candidate type does not implement daughter(std::string). "
0394 << "Please use CompositeCandidate or NamedCompositeCandidate.\n";
0395 }
0396
0397 reco::Candidate *pat::PackedCandidate::daughter(const std::string &) {
0398 throw edm::Exception(edm::errors::UnimplementedFeature)
0399 << "This Candidate type does not implement daughter(std::string). "
0400 << "Please use CompositeCandidate or NamedCompositeCandidate.\n";
0401 }
0402
0403 reco::Candidate *pat::PackedCandidate::daughter(size_type) { return nullptr; }
0404
0405 double pat::PackedCandidate::vertexChi2() const { return 0; }
0406
0407 double pat::PackedCandidate::vertexNdof() const { return 0; }
0408
0409 double pat::PackedCandidate::vertexNormalizedChi2() const { return 0; }
0410
0411 double pat::PackedCandidate::vertexCovariance(int i, int j) const {
0412 throw edm::Exception(edm::errors::UnimplementedFeature)
0413 << "reco::ConcreteCandidate does not implement vertex covariant "
0414 "matrix.\n";
0415 }
0416
0417 void pat::PackedCandidate::fillVertexCovariance(CovarianceMatrix &err) const {
0418 throw edm::Exception(edm::errors::UnimplementedFeature)
0419 << "reco::ConcreteCandidate does not implement vertex covariant "
0420 "matrix.\n";
0421 }
0422
0423 bool pat::PackedCandidate::longLived() const { return false; }
0424
0425 bool pat::PackedCandidate::massConstraint() const { return false; }
0426
0427
0428 void pat::PackedCandidate::setPuppiWeight(float p, float p_nolep) {
0429
0430
0431 packedPuppiweight_ = std::numeric_limits<uint8_t>::max() * p;
0432 packedPuppiweightNoLepDiff_ = std::numeric_limits<int8_t>::max() * (p_nolep - p);
0433 }
0434
0435 float pat::PackedCandidate::puppiWeight() const {
0436 return 1.f * packedPuppiweight_ / std::numeric_limits<uint8_t>::max();
0437 }
0438
0439 float pat::PackedCandidate::puppiWeightNoLep() const {
0440 return 1.f * packedPuppiweightNoLepDiff_ / std::numeric_limits<int8_t>::max() +
0441 1.f * packedPuppiweight_ / std::numeric_limits<uint8_t>::max();
0442 }
0443
0444 void pat::PackedCandidate::setRawCaloFraction(float p) {
0445 if (100 * p > std::numeric_limits<uint8_t>::max())
0446 rawCaloFraction_ = std::numeric_limits<uint8_t>::max();
0447 else
0448 rawCaloFraction_ = 100 * p;
0449 }
0450
0451 void pat::PackedCandidate::setRawHcalFraction(float p) { rawHcalFraction_ = 100 * p; }
0452
0453 void pat::PackedCandidate::setCaloFraction(float p) { caloFraction_ = 100 * p; }
0454
0455 void pat::PackedCandidate::setHcalFraction(float p) { hcalFraction_ = 100 * p; }
0456
0457 void pat::PackedCandidate::setIsIsolatedChargedHadron(bool p) { isIsolatedChargedHadron_ = p; }
0458
0459 void pat::PackedCandidate::setDTimeAssociatedPV(float aTime, float aTimeError) {
0460 if (aTime == 0 && aTimeError == 0) {
0461 packedTime_ = 0;
0462 packedTimeError_ = 0;
0463 } else if (aTimeError == 0) {
0464 packedTimeError_ = 0;
0465 packedTime_ = packTimeNoError(aTime);
0466 } else {
0467 packedTimeError_ = packTimeError(aTimeError);
0468 aTimeError = unpackTimeError(packedTimeError_);
0469 packedTime_ = packTimeWithError(aTime, aTimeError);
0470 }
0471 }
0472
0473
0474 uint8_t pat::PackedCandidate::packTimeError(float timeError) {
0475 if (timeError <= 0)
0476 return 0;
0477
0478
0479
0480
0481
0482 return std::max<uint8_t>(
0483 std::min(std::round(std::ldexp(std::log2(timeError / MIN_TIMEERROR), +EXPO_TIMEERROR)), 255.f), 1);
0484 }
0485 float pat::PackedCandidate::unpackTimeError(uint8_t timeError) {
0486 return timeError > 0 ? MIN_TIMEERROR * std::exp2(std::ldexp(float(timeError), -EXPO_TIMEERROR)) : -1.0f;
0487 }
0488 float pat::PackedCandidate::unpackTimeNoError(int16_t time) {
0489 if (time == 0)
0490 return 0.f;
0491 return (time > 0 ? MIN_TIME_NOERROR : -MIN_TIME_NOERROR) *
0492 std::exp2(std::ldexp(float(std::abs(time)), -EXPO_TIME_NOERROR));
0493 }
0494 int16_t pat::PackedCandidate::packTimeNoError(float time) {
0495
0496
0497
0498
0499
0500
0501 if (std::abs(time) < MIN_TIME_NOERROR)
0502 return 0;
0503 float fpacked = std::ldexp(std::log2(std::abs(time / MIN_TIME_NOERROR)), +EXPO_TIME_NOERROR);
0504 return (time > 0 ? +1 : -1) * std::min(std::round(fpacked), 2047.f);
0505 }
0506 float pat::PackedCandidate::unpackTimeWithError(int16_t time, uint8_t timeError) {
0507 if (time % 2 == 0) {
0508
0509 return std::ldexp(unpackTimeError(timeError), EXPO_TIME_WITHERROR) * float(time / 2);
0510 } else {
0511
0512 return pat::PackedCandidate::unpackTimeNoError(time / 2);
0513 }
0514 }
0515 int16_t pat::PackedCandidate::packTimeWithError(float time, float timeError) {
0516
0517
0518
0519
0520
0521
0522
0523 float fpacked = std::round(time / std::ldexp(timeError, EXPO_TIME_WITHERROR));
0524 if (std::abs(fpacked) < 16383.f) {
0525
0526 return int16_t(fpacked) * 2;
0527 } else {
0528 int16_t packed = packTimeNoError(time);
0529 return packed * 2 + (time > 0 ? +1 : -1);
0530 }
0531 }