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