diff --git a/openpilot/cereal/custom.capnp b/openpilot/cereal/custom.capnp index 9ce42edb2f..38ec63cff8 100644 --- a/openpilot/cereal/custom.capnp +++ b/openpilot/cereal/custom.capnp @@ -1233,6 +1233,7 @@ struct AutomaticLearningSourceReceipt { validUntilMonoTime @6 :UInt64; cpSha256 @7 :Text; calibrationBasis @8 :List(Float64); + calibrationGeneration @9 :Text; } struct LearningApplication { diff --git a/openpilot/cereal/gen/cpp/custom.capnp.c++ b/openpilot/cereal/gen/cpp/custom.capnp.c++ index 77dbc8ea44..1f0b10b95d 100644 --- a/openpilot/cereal/gen/cpp/custom.capnp.c++ +++ b/openpilot/cereal/gen/cpp/custom.capnp.c++ @@ -16136,17 +16136,17 @@ const ::capnp::_::RawSchema s_ccffb4ab144bf131 = { 0, 2, i_ccffb4ab144bf131, nullptr, nullptr, { &s_ccffb4ab144bf131, nullptr, nullptr, 0, 0, nullptr }, false }; #endif // !CAPNP_LITE -static const ::capnp::_::AlignedData<168> b_9fe2d23e75805149 = { +static const ::capnp::_::AlignedData<185> b_9fe2d23e75805149 = { { 0, 0, 0, 0, 5, 0, 6, 0, 73, 81, 128, 117, 62, 210, 226, 159, 13, 0, 0, 0, 1, 0, 3, 0, 89, 10, 85, 29, 102, 186, 38, 181, - 4, 0, 7, 0, 0, 0, 0, 0, + 5, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 98, 1, 0, 0, 41, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 37, 0, 0, 0, 255, 1, 0, 0, + 37, 0, 0, 0, 55, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 117, 115, 116, 111, 109, 46, 99, @@ -16156,70 +16156,77 @@ static const ::capnp::_::AlignedData<168> b_9fe2d23e75805149 = { 117, 114, 99, 101, 82, 101, 99, 101, 105, 112, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, - 36, 0, 0, 0, 3, 0, 4, 0, + 40, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 237, 0, 0, 0, 66, 0, 0, 0, + 9, 1, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 232, 0, 0, 0, 3, 0, 1, 0, - 244, 0, 0, 0, 2, 0, 1, 0, + 4, 1, 0, 0, 3, 0, 1, 0, + 16, 1, 0, 0, 2, 0, 1, 0, 1, 0, 0, 0, 16, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 241, 0, 0, 0, 66, 0, 0, 0, + 13, 1, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 236, 0, 0, 0, 3, 0, 1, 0, - 248, 0, 0, 0, 2, 0, 1, 0, + 8, 1, 0, 0, 3, 0, 1, 0, + 20, 1, 0, 0, 2, 0, 1, 0, 2, 0, 0, 0, 17, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 245, 0, 0, 0, 58, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 240, 0, 0, 0, 3, 0, 1, 0, - 252, 0, 0, 0, 2, 0, 1, 0, - 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 249, 0, 0, 0, 130, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 248, 0, 0, 0, 3, 0, 1, 0, - 4, 1, 0, 0, 2, 0, 1, 0, - 4, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 98, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 3, 0, 1, 0, - 12, 1, 0, 0, 2, 0, 1, 0, - 5, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 0, 5, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 9, 1, 0, 0, 138, 0, 0, 0, + 17, 1, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 1, 0, 0, 3, 0, 1, 0, 24, 1, 0, 0, 2, 0, 1, 0, + 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 21, 1, 0, 0, 130, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 20, 1, 0, 0, 3, 0, 1, 0, + 32, 1, 0, 0, 2, 0, 1, 0, + 4, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 29, 1, 0, 0, 98, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 28, 1, 0, 0, 3, 0, 1, 0, + 40, 1, 0, 0, 2, 0, 1, 0, + 5, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 37, 1, 0, 0, 138, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 40, 1, 0, 0, 3, 0, 1, 0, + 52, 1, 0, 0, 2, 0, 1, 0, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 21, 1, 0, 0, 154, 0, 0, 0, + 49, 1, 0, 0, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 24, 1, 0, 0, 3, 0, 1, 0, - 36, 1, 0, 0, 2, 0, 1, 0, + 52, 1, 0, 0, 3, 0, 1, 0, + 64, 1, 0, 0, 2, 0, 1, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 1, 0, 0, 74, 0, 0, 0, + 61, 1, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 1, 0, 0, 3, 0, 1, 0, - 44, 1, 0, 0, 2, 0, 1, 0, + 60, 1, 0, 0, 3, 0, 1, 0, + 72, 1, 0, 0, 2, 0, 1, 0, 8, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 41, 1, 0, 0, 138, 0, 0, 0, + 69, 1, 0, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 44, 1, 0, 0, 3, 0, 1, 0, - 72, 1, 0, 0, 2, 0, 1, 0, + 72, 1, 0, 0, 3, 0, 1, 0, + 100, 1, 0, 0, 2, 0, 1, 0, + 9, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 1, 0, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 97, 1, 0, 0, 178, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 100, 1, 0, 0, 3, 0, 1, 0, + 112, 1, 0, 0, 2, 0, 1, 0, 118, 101, 114, 115, 105, 111, 110, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -16303,16 +16310,26 @@ static const ::capnp::_::AlignedData<168> b_9fe2d23e75805149 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 99, 97, 108, 105, 98, 114, 97, 116, + 105, 111, 110, 71, 101, 110, 101, 114, + 97, 116, 105, 111, 110, 0, 0, 0, + 12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } }; ::capnp::word const* const bp_9fe2d23e75805149 = b_9fe2d23e75805149.words; #if !CAPNP_LITE -static const uint16_t m_9fe2d23e75805149[] = {8, 7, 1, 2, 5, 3, 4, 6, 0}; -static const uint16_t i_9fe2d23e75805149[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; +static const uint16_t m_9fe2d23e75805149[] = {8, 9, 7, 1, 2, 5, 3, 4, 6, 0}; +static const uint16_t i_9fe2d23e75805149[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; const ::capnp::_::RawSchema s_9fe2d23e75805149 = { - 0x9fe2d23e75805149, b_9fe2d23e75805149.words, 168, nullptr, m_9fe2d23e75805149, - 0, 9, i_9fe2d23e75805149, nullptr, nullptr, { &s_9fe2d23e75805149, nullptr, nullptr, 0, 0, nullptr }, false + 0x9fe2d23e75805149, b_9fe2d23e75805149.words, 185, nullptr, m_9fe2d23e75805149, + 0, 10, i_9fe2d23e75805149, nullptr, nullptr, { &s_9fe2d23e75805149, nullptr, nullptr, 0, 0, nullptr }, false }; #endif // !CAPNP_LITE static const ::capnp::_::AlignedData<168> b_82b6c8ea6a6efabd = { diff --git a/openpilot/cereal/gen/cpp/custom.capnp.h b/openpilot/cereal/gen/cpp/custom.capnp.h index cddfc7387e..ea8f135c62 100644 --- a/openpilot/cereal/gen/cpp/custom.capnp.h +++ b/openpilot/cereal/gen/cpp/custom.capnp.h @@ -1502,7 +1502,7 @@ struct AutomaticLearningSourceReceipt { class Pipeline; struct _capnpPrivate { - CAPNP_DECLARE_STRUCT_HEADER(9fe2d23e75805149, 3, 4) + CAPNP_DECLARE_STRUCT_HEADER(9fe2d23e75805149, 3, 5) #if !CAPNP_LITE static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; } #endif // !CAPNP_LITE @@ -10792,6 +10792,9 @@ public: inline bool hasCalibrationBasis() const; inline ::capnp::List::Reader getCalibrationBasis() const; + inline bool hasCalibrationGeneration() const; + inline ::capnp::Text::Reader getCalibrationGeneration() const; + private: ::capnp::_::StructReader _reader; template @@ -10864,6 +10867,13 @@ public: inline void adoptCalibrationBasis(::capnp::Orphan< ::capnp::List>&& value); inline ::capnp::Orphan< ::capnp::List> disownCalibrationBasis(); + inline bool hasCalibrationGeneration(); + inline ::capnp::Text::Builder getCalibrationGeneration(); + inline void setCalibrationGeneration( ::capnp::Text::Reader value); + inline ::capnp::Text::Builder initCalibrationGeneration(unsigned int size); + inline void adoptCalibrationGeneration(::capnp::Orphan< ::capnp::Text>&& value); + inline ::capnp::Orphan< ::capnp::Text> disownCalibrationGeneration(); + private: ::capnp::_::StructBuilder _builder; template @@ -26365,6 +26375,40 @@ inline ::capnp::Orphan< ::capnp::List> Automa ::capnp::bounded<3>() * ::capnp::POINTERS)); } +inline bool AutomaticLearningSourceReceipt::Reader::hasCalibrationGeneration() const { + return !_reader.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS).isNull(); +} +inline bool AutomaticLearningSourceReceipt::Builder::hasCalibrationGeneration() { + return !_builder.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS).isNull(); +} +inline ::capnp::Text::Reader AutomaticLearningSourceReceipt::Reader::getCalibrationGeneration() const { + return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_reader.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS)); +} +inline ::capnp::Text::Builder AutomaticLearningSourceReceipt::Builder::getCalibrationGeneration() { + return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_builder.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS)); +} +inline void AutomaticLearningSourceReceipt::Builder::setCalibrationGeneration( ::capnp::Text::Reader value) { + ::capnp::_::PointerHelpers< ::capnp::Text>::set(_builder.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS), value); +} +inline ::capnp::Text::Builder AutomaticLearningSourceReceipt::Builder::initCalibrationGeneration(unsigned int size) { + return ::capnp::_::PointerHelpers< ::capnp::Text>::init(_builder.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS), size); +} +inline void AutomaticLearningSourceReceipt::Builder::adoptCalibrationGeneration( + ::capnp::Orphan< ::capnp::Text>&& value) { + ::capnp::_::PointerHelpers< ::capnp::Text>::adopt(_builder.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS), kj::mv(value)); +} +inline ::capnp::Orphan< ::capnp::Text> AutomaticLearningSourceReceipt::Builder::disownCalibrationGeneration() { + return ::capnp::_::PointerHelpers< ::capnp::Text>::disown(_builder.getPointerField( + ::capnp::bounded<4>() * ::capnp::POINTERS)); +} + inline ::uint16_t LearningApplication::Reader::getVersion() const { return _reader.getDataField< ::uint16_t>( ::capnp::bounded<0>() * ::capnp::ELEMENTS); diff --git a/openpilot/cereal/libcereal.a b/openpilot/cereal/libcereal.a index c0d4180ae7..d5a6a17a7a 100644 Binary files a/openpilot/cereal/libcereal.a and b/openpilot/cereal/libcereal.a differ diff --git a/openpilot/selfdrive/pandad/pandad b/openpilot/selfdrive/pandad/pandad index 6339681268..9af5a51d8f 100755 Binary files a/openpilot/selfdrive/pandad/pandad and b/openpilot/selfdrive/pandad/pandad differ diff --git a/openpilot/starpilot/flm/calibration_lifecycle.py b/openpilot/starpilot/flm/calibration_lifecycle.py new file mode 100644 index 0000000000..a97d7a537a --- /dev/null +++ b/openpilot/starpilot/flm/calibration_lifecycle.py @@ -0,0 +1,109 @@ +"""Calibration estimates share a vehicle frame until the producer resets it.""" +import math +import uuid + + +def calibrated_basis(message): + try: + basis_values, spread_values = message.rpyCalib, message.rpyCalibSpread + if len(basis_values) != 3 or len(spread_values) != 3: + return None + basis = tuple(float(v) for v in basis_values) + blocks = message.validBlocks + if type(blocks) is not int: + return None + spread = tuple(float(v) for v in spread_values) + if (str(message.calStatus) != 'calibrated' or blocks < 5 or blocks > 50 or + len(basis) != 3 or not all(math.isfinite(v) for v in basis) or + len(spread) != 3 or not all(math.isfinite(v) and v >= 0 for v in spread) or + spread[1] > math.radians(4) or spread[2] > math.radians(2)): + return None + return basis, blocks + except (AttributeError, TypeError, ValueError, OverflowError): + return None + + +def generation_valid(value): + return type(value) is str and len(value) == 32 and all(c in '0123456789abcdef' for c in value) + + +class ProducerCalibrationLifecycle: + def __init__(self, token_factory=lambda: uuid.uuid4().hex): + self.token_factory = token_factory + self.token = '' + self.blocks = None + self.issued = set() + + def observe(self, message, *, current): + value = calibrated_basis(message) if current else None + if value is None: + self.token = '' + self.blocks = None + return None + basis, blocks = value + if self.blocks is not None and blocks < self.blocks: + self.token = '' + if not self.token: + if len(self.issued) >= 32: + return None # Bounded reset history; never recycle an old generation. + try: + token = self.token_factory() + except Exception: + return None + if not generation_valid(token) or token in self.issued: + return None + self.token = token + self.issued.add(token) + self.blocks = blocks + return self.token, basis + + +class ConsumerCalibrationLifecycle: + def __init__(self): + self.generation = '' + self.blocks = None + self.rejected = set() + self.exhausted = False + self.current = False + self.last_invalid_ns = 0 + + def retire(self, generation): + if generation and generation not in self.rejected: + if len(self.rejected) >= 32: + self.exhausted = True + else: + self.rejected.add(generation) + + def capture(self, generation): + if not generation_valid(generation): + return False + if self.generation and generation != self.generation: + self.retire(self.generation) + self.generation = generation + return not self.exhausted and generation not in self.rejected + + def reject(self, *, now_ns=0): + self.retire(self.generation) + if type(now_ns) is int and now_ns > self.last_invalid_ns: + self.last_invalid_ns = now_ns + self.blocks = None + self.current = False + + def observe(self, message, *, current, now_ns=0): + value = calibrated_basis(message) if current else None + if value is None: + self.reject(now_ns=now_ns) + return False + _, blocks = value + if self.blocks is not None and blocks < self.blocks: + self.reject(now_ns=now_ns) + self.blocks = blocks + self.current = True + return True + + def admit(self, generation, observed_ns): + if (not self.current or self.exhausted or not generation_valid(generation) or + type(observed_ns) is not int or observed_ns <= self.last_invalid_ns or + generation in self.rejected): + return False + return self.capture(generation) diff --git a/openpilot/starpilot/flm/global_runtime.py b/openpilot/starpilot/flm/global_runtime.py index 8629c5b974..5fce413636 100644 --- a/openpilot/starpilot/flm/global_runtime.py +++ b/openpilot/starpilot/flm/global_runtime.py @@ -42,6 +42,8 @@ class GlobalRuntime: def __init__(self, cp, ci, controller): # Construct AFTER actual LaC selection, startup native/manual configuration. self.cp, self.ci, self.controller = cp, ci, controller + from openpilot.starpilot.flm.calibration_lifecycle import ConsumerCalibrationLifecycle + self._calibration_lifecycle = ConsumerCalibrationLifecycle() self.health_services = ('carState', 'modelV2', 'vehicleParameters', 'lateralDelay', 'driverMonitoringState', 'extrinsicsCalibration') try: self.descriptor = selected_descriptor(cp, ci, controller) @@ -114,6 +116,29 @@ class GlobalRuntime: """Bounded current source/admission checks; invoked immediately before LaC.""" self.used, self.stage, self.frame, self.eligible = False, None, None, False self.source = None + # Capture a fresh V2 generation and calibration interruption before ANY + # unrelated adapter/native/vehicle health gate can return. + tracker = self._calibration_lifecycle + early_source = None + try: + if (type(now_ns) is int and now_ns > 0 and self.descriptor is not None and + sm.seen[SERVICE] and sm.alive[SERVICE] and sm.valid[SERVICE] and sm.freq_ok[SERVICE] and + 0 <= now_ns - int(sm.recv_time[SERVICE] * 1e9) <= SOURCE_LEASE_NS): + envelope = sm[SERVICE] + if envelope.learning.sourceReceipt.version == 2: + early_source = read_source_receipt(SimpleNamespace(valid=True, autoSteeringLearning=envelope), + now_ns=now_ns, cp_sha256=self.descriptor.cp_sha256) + if early_source is not None and not tracker.generation: + tracker.capture(early_source.calibration_generation) + stamp = int(sm.logMonoTime['extrinsicsCalibration']) + receipt = int(sm.recv_time['extrinsicsCalibration'] * 1e9) + current_calibration = bool(type(now_ns) is int and now_ns > 0 and + sm.all_checks(['extrinsicsCalibration']) and + 0 < stamp <= now_ns <= stamp + 500_000_000 and + 0 < receipt <= now_ns <= receipt + 500_000_000) + tracker.observe(sm['extrinsicsCalibration'], current=current_calibration, now_ns=now_ns) + except (AttributeError, KeyError, TypeError, ValueError, OverflowError): + tracker.reject(now_ns=now_ns) self.outcome = None self.centre_frame = None self.centre_raw = () @@ -167,6 +192,8 @@ class GlobalRuntime: stamp = int(sm.logMonoTime[service]) receipt = int(sm.recv_time[service] * 1e9) if not (sm.seen[service] and 0 < stamp <= now_ns <= stamp + maximum and 0 < receipt <= now_ns <= receipt + maximum): + if service == 'extrinsicsCalibration' and hasattr(self, '_calibration_lifecycle'): + self._calibration_lifecycle.reject() raise ValueError('stale_original_source') if self.controller.sat_time > 0: raise ValueError('controller_saturation') @@ -177,7 +204,7 @@ class GlobalRuntime: raise ValueError('stale_receipt') envelope = sm[SERVICE] event = SimpleNamespace(valid=True, autoSteeringLearning=envelope) - source = read_source_receipt(event, now_ns=now_ns, cp_sha256=self.descriptor.cp_sha256) + source = early_source if early_source is not None else read_source_receipt(event, now_ns=now_ns, cp_sha256=self.descriptor.cp_sha256) if ( source is None or source.cp_sha256 != self.descriptor.cp_sha256 @@ -185,7 +212,11 @@ class GlobalRuntime: ): raise ValueError('source_identity_or_clock') calibration = sm['extrinsicsCalibration'] - if str(calibration.calStatus) != 'calibrated' or source.calibration != tuple(float(value) for value in calibration.rpyCalib): + if source.calibration_generation: + if (len(source.calibration) != 3 or not all(math.isfinite(value) for value in source.calibration) or + not tracker.admit(source.calibration_generation, source.observed_ns)): + raise ValueError('calibration_identity_changed') + elif str(calibration.calStatus) != 'calibrated' or source.calibration != tuple(float(value) for value in calibration.rpyCalib): raise ValueError('calibration_identity_changed') if not self.settings_cache.install(source, received_ns=received) or self.settings_cache.current(now_ns=now_ns) is None: raise ValueError('source_cache') diff --git a/openpilot/starpilot/flm/learning_wire.py b/openpilot/starpilot/flm/learning_wire.py index 706fd5bb84..0f8408ba15 100644 --- a/openpilot/starpilot/flm/learning_wire.py +++ b/openpilot/starpilot/flm/learning_wire.py @@ -223,6 +223,11 @@ def result_message(result, session, sequence, now_ns, *, replay_validation=None) raise ValueError('profile bound') if any(not finite(p.excitation_floor) or p.excitation_floor <= 0 for p in profile.parameters): raise ValueError('profile excitation floor') + if any(not all(finite(v) for v in (p.low, p.high, p.max_step, p.slew_per_s, coefficient)) + or p.low > p.high or p.max_step <= 0 or p.slew_per_s <= 0 + or not p.low <= coefficient <= p.high or abs(coefficient) > p.max_step + for p, coefficient in zip(profile.parameters, profile.coefficients, strict=True)): + raise ValueError('profile parameter bound') if not replay_accepts(replay_validation, profile): raise ValueError('actual replay validation required') value.status = 'qualifiedFit' if result.status == 'qualified' else 'rejected' @@ -331,10 +336,12 @@ def write_source_receipt(event, source): not identity(source.cp_sha256) or type(source.observed_ns) is not int or not 0 < source.observed_ns <= source.expires_ns <= source.observed_ns + 500_000_000 ): raise ValueError('source receipt bounds') + if source.calibration_generation and not _calibration_generation(source.calibration_generation): + raise ValueError('calibration lifecycle generation') if source.enabled and (not identity(source.source_epoch) or not source.reset): raise ValueError('enabled source receipt identity') event.autoSteeringLearning.learning.sourceReceipt = { - 'version': VERSION, + 'version': 2 if source.calibration_generation else VERSION, 'enabled': source.enabled, 'manual': source.manual, 'resetGeneration': source.reset, @@ -343,11 +350,13 @@ def write_source_receipt(event, source): 'validUntilMonoTime': source.expires_ns, 'cpSha256': source.cp_sha256, 'calibrationBasis': source.calibration, + 'calibrationGeneration': source.calibration_generation, } def _read_native_source_receipt(row, now_ns, cp_sha256, source_type): - if row.version != VERSION: + version = row.version + if version not in (VERSION, 2): return None cp = str(row.cpSha256) if cp != cp_sha256 or not identity(cp): @@ -382,7 +391,14 @@ def _read_native_source_receipt(row, now_ns, cp_sha256, source_type): manual = bool(row.manual) if not enabled: epoch = str(row.sourceEpoch) - return source_type(enabled, manual, reset, epoch, observed, expires, cp, tuple(calibration)) + generation = str(row.calibrationGeneration) if version == 2 else '' + if version == 2 and not _calibration_generation(generation): + return None + return source_type(enabled, manual, reset, epoch, observed, expires, cp, tuple(calibration), generation) + + +def _calibration_generation(value): + return type(value) is str and len(value) == 32 and all(c in '0123456789abcdef' for c in value) def read_source_receipt(event, *, now_ns, cp_sha256): @@ -394,8 +410,11 @@ def read_source_receipt(event, *, now_ns, cp_sha256): if (type(row) is _DynamicStructReader and row.schema == custom.AutomaticLearningSourceReceipt.schema and type(now_ns) is int and type(cp_sha256) is str): return _read_native_source_receipt(row, now_ns, cp_sha256, SourceState) + version = row.version + if version == 2: + return _read_native_source_receipt(row, now_ns, cp_sha256, SourceState) if ( - row.version != VERSION + version != VERSION or str(row.cpSha256) != cp_sha256 or not identity(str(row.cpSha256)) or not 0 < row.observedMonoTime <= now_ns <= row.validUntilMonoTime <= row.observedMonoTime + 500_000_000 diff --git a/openpilot/starpilot/flm/runtime_adapter_registry.py b/openpilot/starpilot/flm/runtime_adapter_registry.py index 4521c7c677..325821ed9e 100644 --- a/openpilot/starpilot/flm/runtime_adapter_registry.py +++ b/openpilot/starpilot/flm/runtime_adapter_registry.py @@ -235,9 +235,10 @@ class SourceState: expires_ns: int cp_sha256: str calibration: tuple[float, ...] = () + calibration_generation: str = '' -def read_source(cp, params, *, now_ns, cp_sha256=None, calibration=(), vehicle_session=""): +def read_source(cp, params, *, now_ns, cp_sha256=None, calibration=(), vehicle_session="", calibration_generation=''): """Only startup/separate-process settings work; errors disable learning.""" cp_sha256 = car_params_digest(cp) if cp_sha256 is None else cp_sha256 try: @@ -294,8 +295,13 @@ def read_source(cp, params, *, now_ns, cp_sha256=None, calibration=(), vehicle_s unit = vehicle_session else: unit = vin - epoch = digest((tuple((key, None if raw is None else raw.hex()) for key, raw in snapshots), calibration, unit)) - return SourceState(bool(preference.valid and preference.enabled), manual, reset, epoch, now_ns, now_ns + SOURCE_LEASE_NS, cp_sha256, calibration) + if type(calibration_generation) is not str or (calibration_generation and ( + len(calibration_generation) != 32 or any(c not in '0123456789abcdef' for c in calibration_generation))): + raise ValueError('invalid calibration lifecycle') + frame_identity = calibration if not calibration_generation else ('live_vehicle_frame_v2', calibration_generation) + epoch = digest((tuple((key, None if raw is None else raw.hex()) for key, raw in snapshots), frame_identity, unit)) + return SourceState(bool(preference.valid and preference.enabled), manual, reset, epoch, now_ns, + now_ns + SOURCE_LEASE_NS, cp_sha256, calibration, calibration_generation) except (AttributeError, UnicodeError, TypeError, ValueError, OSError, OverflowError): return SourceState(False, False, '', '', now_ns, now_ns + SOURCE_LEASE_NS, cp_sha256) @@ -464,12 +470,19 @@ def physical_observer(sm, cp, collector, now_ns): state_ns, delay_ns = int(sm.logMonoTime['carState']), int(sm.logMonoTime['lateralDelay']) if not 0 < state_ns <= now_ns or now_ns - state_ns > 100_000_000 or not 0 < delay_ns <= now_ns or now_ns - delay_ns > 500_000_000: return None + from openpilot.starpilot.flm.calibration_lifecycle import calibrated_basis generation = digest(tuple(float(v) for v in calibration.rpyCalib)) + estimate = generation + lifecycle = getattr(collector, '_calibration_generation', '') + if lifecycle: + if calibrated_basis(calibration) is None: + return None + generation = digest(('live_vehicle_frame_v2', lifecycle)) cache = getattr(collector, '_physical_calibrator', None) - if cache is None or cache[0] != generation: + if cache is None or cache[0] != estimate: calibrator = PoseCalibrator() calibrator.feed_extrinsics_calibration(calibration) - collector._physical_calibrator = (generation, calibrator) + collector._physical_calibrator = (estimate, calibrator) calibrator = collector._physical_calibrator[1] if not calibrator.calib_valid: return None diff --git a/openpilot/starpilot/flm/steerlearnerd.py b/openpilot/starpilot/flm/steerlearnerd.py index 1a538d0d9d..72143a8908 100644 --- a/openpilot/starpilot/flm/steerlearnerd.py +++ b/openpilot/starpilot/flm/steerlearnerd.py @@ -125,6 +125,7 @@ def run( unavailable_reason = 'actual_adapter_unavailable' return collector = FullrateCollector(adapter) + collector._calibration_generation = source.calibration_generation collector.adapter.select_objective(active_key) # This records primitive actual frames/checkpoints ONLY. No selected # controller update, baseline probes or numerical fitting at100Hz. @@ -260,26 +261,34 @@ def main(): cp_sha256 = car_params_digest(CP) source_cache = [None] vehicle_session = uuid.uuid4().hex + from openpilot.starpilot.flm.calibration_lifecycle import ProducerCalibrationLifecycle + calibration_lifecycle = ProducerCalibrationLifecycle() - def calibration_basis(): - calibration = sm["extrinsicsCalibration"] - if not ( - sm.seen["extrinsicsCalibration"] - and sm.valid["extrinsicsCalibration"] - and sm.alive["extrinsicsCalibration"] - and str(calibration.calStatus) == "calibrated" - ): - return () - return tuple(float(value) for value in calibration.rpyCalib) + def calibration_frame(now): + stamp = int(sm.logMonoTime['extrinsicsCalibration']) + receipt = int(sm.recv_time['extrinsicsCalibration'] * 1e9) + current = bool(sm.all_checks(['extrinsicsCalibration']) and + 0 < stamp <= now <= stamp + 500_000_000 and + 0 < receipt <= now <= receipt + 500_000_000) + return calibration_lifecycle.observe(sm['extrinsicsCalibration'], current=current) + + def current_source(now, frame): + return read_source(CP, params, now_ns=now, cp_sha256=cp_sha256, + calibration=() if frame is None else frame[1], vehicle_session=vehicle_session, + calibration_generation='' if frame is None else frame[0]) def source_reader(now): - # Read once each4Hz receipt renewal, outside all controller/torqued processes. - if source_cache[0] is None or now - source_cache[0].observed_ns >= 250_000_000: - source_cache[0] = read_source(CP, params, now_ns=now, cp_sha256=cp_sha256, calibration=calibration_basis(), vehicle_session=vehicle_session) + # Observe resets on every loop, but keep settings reads at receipt-renewal rate. + frame = calibration_frame(now) + generation = '' if frame is None else frame[0] + if (source_cache[0] is None or generation != source_cache[0].calibration_generation or + now - source_cache[0].observed_ns >= 250_000_000): + source_cache[0] = current_source(now, frame) return source_cache[0] def current_sources(identity, epoch): - current = read_source(CP, params, now_ns=time.monotonic_ns(), cp_sha256=cp_sha256, calibration=calibration_basis(), vehicle_session=vehicle_session) + now = time.monotonic_ns() + current = current_source(now, calibration_frame(now)) return (identity if persistent_unit and epoch == current.source_epoch else '', current.enabled, current.manual, current.reset) persistence = PersistenceModule(params, current_sources) diff --git a/openpilot/starpilot/flm/tests/test_automatic_integration.py b/openpilot/starpilot/flm/tests/test_automatic_integration.py index 06eaa5cef5..35b72e1e1b 100644 --- a/openpilot/starpilot/flm/tests/test_automatic_integration.py +++ b/openpilot/starpilot/flm/tests/test_automatic_integration.py @@ -1,225 +1,130 @@ -"""Composed automatic fit application; synthetic evidence, actual owners and wire.""" - -from contextlib import contextmanager +"""Independent physical fit/replay into current bounded Controls learning stages.""" from dataclasses import replace import json import math -import os from pathlib import Path -import struct import unittest -from unittest import mock from opendbc.car.car_helpers import interfaces -from opendbc.car.gm.values import CAR as GM -from opendbc.car.hyundai.values import CAR as HKG -from openpilot.cereal import messaging -from openpilot.common.params import Params -from openpilot.common.prefix import OpenpilotPrefix -from openpilot.selfdrive.controls.controlsd import Controls -from openpilot.starpilot.flm.automatic import identity_digest -from openpilot.starpilot.flm.learning_producer import AutomaticLearningProducer -from openpilot.starpilot.flm.learning_source import ( - AutomaticLearningConsumer, MAX_AGE_NS, RESET_KEY, SERVICE, learning_payload, -) -from openpilot.starpilot.flm.tests import test_automatic as evidence -from openpilot.starpilot.lateral.controller_selection import ControllerMode, DOCUMENT_KEY as CONTROLLER_KEY, policy_for, replace_mode +from opendbc.car.toyota.values import CAR +from openpilot.starpilot.flm.learning_wire import result_message +from openpilot.starpilot.flm.tests.test_registered_controls_global import controls_for +from openpilot.starpilot.flm.tests.test_registered_accepted_profile import computed_registered_fit +from openpilot.starpilot.flm.tests.test_live_calibration_paths import live_frame, BASE from openpilot.starpilot.lateral.learning_preferences import KEY as PREFERENCE_KEY, replace_preference -from openpilot.starpilot.lateral.tests.test_lane_runtime import feed, publish_corolla_params from openpilot.starpilot.lateral.torque_settings import DOCUMENT_KEY as TORQUE_KEY, replace_field, serialize_document -NOW = 400_000_000_000 + +def current_fit(controls): + for tick in range(30): + live_frame(controls, tick, 'a' * 32) + return computed_registered_fit(controls.CP, controls.global_learning, BASE + 300_000_000) -def float32(value): - """The controller's CarParams torque fields are Float32, not Python doubles.""" - return struct.unpack('= 30: + assert runtime.eligible and runtime.reason != 'qualified', (tick, runtime.reason, vars(runtime._calibration_lifecycle)) + identity = runtime.identity if identity is None else identity + assert runtime.identity == identity + assert controls.LaC.torque_params.to_dict() == tune + assert controls.global_learning.applied_coordinates == (0., 0.) + + +@pytest.mark.parametrize('initial', [True, False]) +def test_actual_core_rejects_old_generation_after_calibration_failure_even_on_other_health_return(initial): + cp = interfaces[CAR.TOYOTA_RAV4_TSS2].get_non_essential_params(CAR.TOYOTA_RAV4_TSS2) + with controls_for(cp, 'standard', True) as controls: + if not initial: + for tick in range(30): + live_frame(controls, tick, 'a' * 32) + start = 0 if initial else 30 + live_frame(controls, start, 'a' * 32, status='recalibrating', blocks=1, healthy=False) + for tick in range(start + 1, start + 31): + live_frame(controls, tick, 'a' * 32) + assert not controls.global_learning.eligible + for tick in range(start + 31, start + 91): + live_frame(controls, tick, 'b' * 32) + assert controls.global_learning.eligible, (controls.global_learning.reason, vars(controls.global_learning._calibration_lifecycle)) + live_frame(controls, start + 91, 'a' * 32, publish=True) + assert not controls.global_learning.eligible + + +def test_actual_core_rejects_calibration_block_regression_until_new_generation(): + cp = interfaces[CAR.TOYOTA_RAV4_TSS2].get_non_essential_params(CAR.TOYOTA_RAV4_TSS2) + with controls_for(cp, 'standard', True) as controls: + for tick in range(30): + live_frame(controls, tick, 'a' * 32) + live_frame(controls, 30, 'a' * 32, blocks=5) + assert not controls.global_learning.eligible + for tick in range(31, 201): + live_frame(controls, tick, 'b' * 32, blocks=5) + assert controls.global_learning.eligible, (controls.global_learning.reason, vars(controls.global_learning._calibration_lifecycle)) + + +class PhysicalSources: + def __init__(self, messages, now): + self.messages = messages + self.logMonoTime = dict.fromkeys(messages, now) + self.recv_time = dict.fromkeys(messages, now / 1e9) + + def __getitem__(self, name): + return self.messages[name] + + def all_checks(self, services): + return True + + +def physical_frame(collector, basis, now=BASE): + motion = messaging.new_message('deviceMotion').deviceMotion + motion.timestamp = now + motion.inputsOK = motion.sensorsOK = motion.posenetOK = True + for name, xyz in (('angularVelocityDevice', (.05, .1, .02)), + ('orientationNED', (.01, .02, .03))): + value = getattr(motion, name) + value.valid = True + value.x, value.y, value.z = xyz + cal = messaging.new_message('extrinsicsCalibration').extrinsicsCalibration + cal.calStatus = 'calibrated' + cal.validBlocks = 50 + cal.rpyCalib, cal.rpyCalibSpread = basis, [0., .001, .001] + cs = SimpleNamespace(steeringPressed=False, brakePressed=False, gasPressed=False, + regenBraking=False, buttonEvents=(), canTimeout=False, canValid=True, + steerFaultTemporary=False, steerFaultPermanent=False, accFaulted=False) + sm = PhysicalSources({'deviceMotion': motion, 'extrinsicsCalibration': cal, + 'carState': cs, 'lateralDelay': SimpleNamespace(lateralDelay=.1), + 'driverMonitoringState': SimpleNamespace(noResponseForceDecel=False)}, now) + cp = SimpleNamespace(steerRatio=14., wheelbase=2.9, centerToFront=1.2, mass=2000.) + with mock.patch('openpilot.starpilot.flm.runtime_adapter_registry.time.monotonic_ns', return_value=now), \ + mock.patch('openpilot.starpilot.flm.runtime_adapter_registry.time.clock_gettime_ns', return_value=now): + return physical_observer(sm, cp, collector, now) + + +def test_actual_physical_observer_updates_live_transform_without_splitting_generation(): + command = SimpleNamespace(native_identity='current-native', state_ns=BASE, + replay_inputs=(('vm.sR', 14.), ('vm.cF', 100000.), ('vm.cR', 100000.), + ('vEgo', 30.), ('steeringAngleDeg', 0.))) + collector = SimpleNamespace(context=('identity', 1, 'normalized_torque', 'stage', 'response', 'a' * 64), + _calibration_generation='b' * 32, _learning_current_native_allowed=True, + ready=[(command, SimpleNamespace(native_allowed=True, apply_ns=BASE))], + adapter=SimpleNamespace(command_domain='normalized_torque')) + first = physical_frame(collector, (0., 0., 0.)) + next_frame = physical_frame(collector, (0., .002, .001)) + assert first is not None and next_frame is not None + assert first.calibration == next_frame.calibration + assert first.yaw_rate != next_frame.yaw_rate + assert first.roll_rad != next_frame.roll_rad + assert first.native_allowed and next_frame.native_allowed + + +def test_new_generation_at_block_regression_is_not_permanently_retired(): + cp = interfaces[CAR.TOYOTA_RAV4_TSS2].get_non_essential_params(CAR.TOYOTA_RAV4_TSS2) + with controls_for(cp, 'standard', True) as controls: + for tick in range(30): + live_frame(controls, tick, 'a' * 32) + live_frame(controls, 30, 'b' * 32, blocks=5, publish=True) + # The first reset observation may pause learning, but its new generation + # must be usable on a subsequent fresh receipt. + for tick in range(31, 201): + live_frame(controls, tick, 'b' * 32, blocks=5) + assert controls.global_learning.eligible, (controls.global_learning.reason, vars(controls.global_learning._calibration_lifecycle)) + + +def test_computed_registered_correction_reaches_controller_with_live_calibration_generation(): + from openpilot.starpilot.flm.tests.test_registered_accepted_profile import computed_registered_fit + + cp = interfaces[CAR.TOYOTA_RAV4_TSS2].get_non_essential_params(CAR.TOYOTA_RAV4_TSS2) + with controls_for(cp, 'standard', True) as controls: + for tick in range(30): + live_frame(controls, tick, 'a' * 32) + runtime = controls.global_learning + identity = runtime.identity + tune = controls.LaC.torque_params.to_dict() + result, validation = computed_registered_fit(controls.CP, runtime, BASE + 300_000_000) + assert result.profile is not None and validation is not None + for tick in range(30, 71): + live_frame(controls, tick, 'a' * 32, basis=(0., .00002, -.00002), + result=result, validation=validation, publish=tick % 25 == 0) + assert runtime.identity == identity + assert runtime.reason == 'qualified' + assert any(value != 0 for value in runtime.applied_coordinates) + assert controls.LaC.torque_params.to_dict() == tune diff --git a/openpilot/tools/replay/replay b/openpilot/tools/replay/replay index 50dc343735..fac50d6ac2 100755 Binary files a/openpilot/tools/replay/replay and b/openpilot/tools/replay/replay differ diff --git a/panda/board/obj/body_h7.bin.signed b/panda/board/obj/body_h7.bin.signed index fff8b21e11..f6f8944dc4 100644 Binary files a/panda/board/obj/body_h7.bin.signed and b/panda/board/obj/body_h7.bin.signed differ diff --git a/panda/board/obj/bootstub.body_h7.bin b/panda/board/obj/bootstub.body_h7.bin index ebf456f2c3..cbc7c5bbcd 100755 Binary files a/panda/board/obj/bootstub.body_h7.bin and b/panda/board/obj/bootstub.body_h7.bin differ diff --git a/panda/board/obj/bootstub.panda.bin b/panda/board/obj/bootstub.panda.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda.bin and b/panda/board/obj/bootstub.panda.bin differ diff --git a/panda/board/obj/bootstub.panda_can_ignition_only.bin b/panda/board/obj/bootstub.panda_can_ignition_only.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_h7.bin b/panda/board/obj/bootstub.panda_h7.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7.bin and b/panda/board/obj/bootstub.panda_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_can_ignition_only.bin b/panda/board/obj/bootstub.panda_h7_can_ignition_only.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_h7_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_hkg_remote.bin b/panda/board/obj/bootstub.panda_h7_hkg_remote.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_hkg_remote.bin and b/panda/board/obj/bootstub.panda_h7_hkg_remote.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_hkg_remote_can_ignition_only.bin b/panda/board/obj/bootstub.panda_h7_hkg_remote_can_ignition_only.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_hkg_remote_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_h7_hkg_remote_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_remote.bin b/panda/board/obj/bootstub.panda_h7_remote.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_remote.bin and b/panda/board/obj/bootstub.panda_h7_remote.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_remote_can_ignition_only.bin b/panda/board/obj/bootstub.panda_h7_remote_can_ignition_only.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_remote_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_h7_remote_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_tesla_wake.bin b/panda/board/obj/bootstub.panda_h7_tesla_wake.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_tesla_wake.bin and b/panda/board/obj/bootstub.panda_h7_tesla_wake.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_tesla_wake_can_ignition_only.bin b/panda/board/obj/bootstub.panda_h7_tesla_wake_can_ignition_only.bin index 8d36904eb3..84fadf4a07 100755 Binary files a/panda/board/obj/bootstub.panda_h7_tesla_wake_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_h7_tesla_wake_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_hkg_remote.bin b/panda/board/obj/bootstub.panda_hkg_remote.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_hkg_remote.bin and b/panda/board/obj/bootstub.panda_hkg_remote.bin differ diff --git a/panda/board/obj/bootstub.panda_hkg_remote_can_ignition_only.bin b/panda/board/obj/bootstub.panda_hkg_remote_can_ignition_only.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_hkg_remote_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_hkg_remote_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_remote.bin b/panda/board/obj/bootstub.panda_remote.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_remote.bin and b/panda/board/obj/bootstub.panda_remote.bin differ diff --git a/panda/board/obj/bootstub.panda_remote_can_ignition_only.bin b/panda/board/obj/bootstub.panda_remote_can_ignition_only.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_remote_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_remote_can_ignition_only.bin differ diff --git a/panda/board/obj/bootstub.panda_tesla_wake.bin b/panda/board/obj/bootstub.panda_tesla_wake.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_tesla_wake.bin and b/panda/board/obj/bootstub.panda_tesla_wake.bin differ diff --git a/panda/board/obj/bootstub.panda_tesla_wake_can_ignition_only.bin b/panda/board/obj/bootstub.panda_tesla_wake_can_ignition_only.bin index 75b9124ae0..ff4194a6de 100755 Binary files a/panda/board/obj/bootstub.panda_tesla_wake_can_ignition_only.bin and b/panda/board/obj/bootstub.panda_tesla_wake_can_ignition_only.bin differ diff --git a/panda/board/obj/gitversion.h b/panda/board/obj/gitversion.h index e8c3c5b153..4e90c83bc0 100644 --- a/panda/board/obj/gitversion.h +++ b/panda/board/obj/gitversion.h @@ -1,4 +1,4 @@ #pragma once extern const uint8_t gitversion[29]; -const uint8_t gitversion[29] = "DEV-63008e94-P92eb5651-DEBUG"; +const uint8_t gitversion[29] = "DEV-7e63c553-P92eb5651-DEBUG"; diff --git a/panda/board/obj/panda.bin.signed b/panda/board/obj/panda.bin.signed index 769d6c3e25..6a00b3a915 100644 Binary files a/panda/board/obj/panda.bin.signed and b/panda/board/obj/panda.bin.signed differ diff --git a/panda/board/obj/panda_can_ignition_only.bin.signed b/panda/board/obj/panda_can_ignition_only.bin.signed index d00cd2ae1c..0e771ab0e9 100644 Binary files a/panda/board/obj/panda_can_ignition_only.bin.signed and b/panda/board/obj/panda_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_h7.bin.signed b/panda/board/obj/panda_h7.bin.signed index bec5cddc5f..f61d24f47e 100644 Binary files a/panda/board/obj/panda_h7.bin.signed and b/panda/board/obj/panda_h7.bin.signed differ diff --git a/panda/board/obj/panda_h7_can_ignition_only.bin.signed b/panda/board/obj/panda_h7_can_ignition_only.bin.signed index e8a1c36a75..1ad9eb1b29 100644 Binary files a/panda/board/obj/panda_h7_can_ignition_only.bin.signed and b/panda/board/obj/panda_h7_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_h7_hkg_remote.bin.signed b/panda/board/obj/panda_h7_hkg_remote.bin.signed index e1e00549d7..44bc022ac2 100644 Binary files a/panda/board/obj/panda_h7_hkg_remote.bin.signed and b/panda/board/obj/panda_h7_hkg_remote.bin.signed differ diff --git a/panda/board/obj/panda_h7_hkg_remote_can_ignition_only.bin.signed b/panda/board/obj/panda_h7_hkg_remote_can_ignition_only.bin.signed index 27cf1e2d90..96a2037240 100644 Binary files a/panda/board/obj/panda_h7_hkg_remote_can_ignition_only.bin.signed and b/panda/board/obj/panda_h7_hkg_remote_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_h7_remote.bin.signed b/panda/board/obj/panda_h7_remote.bin.signed index f8c32833f8..16675698ba 100644 Binary files a/panda/board/obj/panda_h7_remote.bin.signed and b/panda/board/obj/panda_h7_remote.bin.signed differ diff --git a/panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed b/panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed index a1fe1abcb7..e3d422c48e 100644 Binary files a/panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed and b/panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_h7_tesla_wake.bin.signed b/panda/board/obj/panda_h7_tesla_wake.bin.signed index 39806534c3..541aebf445 100644 Binary files a/panda/board/obj/panda_h7_tesla_wake.bin.signed and b/panda/board/obj/panda_h7_tesla_wake.bin.signed differ diff --git a/panda/board/obj/panda_h7_tesla_wake_can_ignition_only.bin.signed b/panda/board/obj/panda_h7_tesla_wake_can_ignition_only.bin.signed index 4261a7ac1a..86c97e7fcd 100644 Binary files a/panda/board/obj/panda_h7_tesla_wake_can_ignition_only.bin.signed and b/panda/board/obj/panda_h7_tesla_wake_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_hkg_remote.bin.signed b/panda/board/obj/panda_hkg_remote.bin.signed index c90ef54d0b..e9d660c074 100644 Binary files a/panda/board/obj/panda_hkg_remote.bin.signed and b/panda/board/obj/panda_hkg_remote.bin.signed differ diff --git a/panda/board/obj/panda_hkg_remote_can_ignition_only.bin.signed b/panda/board/obj/panda_hkg_remote_can_ignition_only.bin.signed index da9d29f69c..92c4c6f746 100644 Binary files a/panda/board/obj/panda_hkg_remote_can_ignition_only.bin.signed and b/panda/board/obj/panda_hkg_remote_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_remote.bin.signed b/panda/board/obj/panda_remote.bin.signed index d65c079d60..585b17c7a2 100644 Binary files a/panda/board/obj/panda_remote.bin.signed and b/panda/board/obj/panda_remote.bin.signed differ diff --git a/panda/board/obj/panda_remote_can_ignition_only.bin.signed b/panda/board/obj/panda_remote_can_ignition_only.bin.signed index 4bbfa29b0b..e9403d366f 100644 Binary files a/panda/board/obj/panda_remote_can_ignition_only.bin.signed and b/panda/board/obj/panda_remote_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/panda_tesla_wake.bin.signed b/panda/board/obj/panda_tesla_wake.bin.signed index b96a979e24..50251eabc8 100644 Binary files a/panda/board/obj/panda_tesla_wake.bin.signed and b/panda/board/obj/panda_tesla_wake.bin.signed differ diff --git a/panda/board/obj/panda_tesla_wake_can_ignition_only.bin.signed b/panda/board/obj/panda_tesla_wake_can_ignition_only.bin.signed index 5f44820eea..5631e7abb0 100644 Binary files a/panda/board/obj/panda_tesla_wake_can_ignition_only.bin.signed and b/panda/board/obj/panda_tesla_wake_can_ignition_only.bin.signed differ diff --git a/panda/board/obj/version b/panda/board/obj/version index 83833eb2f7..0789174b57 100644 --- a/panda/board/obj/version +++ b/panda/board/obj/version @@ -1 +1 @@ -DEV-63008e94-P92eb5651-DEBUG \ No newline at end of file +DEV-7e63c553-P92eb5651-DEBUG \ No newline at end of file diff --git a/prebuilt.json b/prebuilt.json index afcec04b4e..79c19dc413 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "0f7ccdbebc3383a1c589b1b73d5e36f6083dc63c14a3d4a25a6f994834ed0fbc", "schema_version": 1} +{"protected_inventory_sha256": "7598dc8a2d426e413747c501920f079c3426a7c37816fcc80fadaccb6ffffcb0", "schema_version": 1}