public RUN_TRANSITION_STATE getRunTransitionState(Context context) {
    final VehicleState state = context.getState();
    final VehicleState parentState = context.getParentState();
    final Observation obs = context.getObservation();
    final Observation prevObs = obs.getPreviousObservation();
    final BlockStateObservation blockStateObs = state.getBlockStateObservation();

    if (parentState != null
        && prevObs != null
        && MotionModelImpl.hasRunChanged(
            parentState.getBlockStateObservation(), state.getBlockStateObservation())) {
      if (!Objects.equal(obs.getRecord().getRunId(), prevObs.getRecord().getRunId())
          || !Objects.equal(obs.getRecord().getOperatorId(), prevObs.getRecord().getOperatorId())) {

        return RUN_TRANSITION_STATE.RUN_CHANGE_INFO_DIFF;
      } else {
        EVehiclePhase parentPhase = parentState.getJourneyState().getPhase();

        /*
         * TODO clean up this hack We are really in-progress, but because of the
         * out-of-service headsign, we can't report it as in-progress
         */
        if (context.getObservation().hasOutOfServiceDsc()
            && EVehiclePhase.DEADHEAD_DURING == parentPhase
            && (blockStateObs != null && blockStateObs.isOnTrip()))
          parentPhase = EVehiclePhase.IN_PROGRESS;

        if (EVehiclePhase.isLayover(parentPhase)
            || EVehiclePhase.DEADHEAD_AFTER == parentPhase
            || EVehiclePhase.DEADHEAD_BEFORE == parentPhase
            || EVehiclePhase.DEADHEAD_DURING == parentPhase) {
          if (parentState.getBlockStateObservation() != null
              && !parentState.getBlockStateObservation().isRunFormal()
              && state.getJourneyState().getPhase() == EVehiclePhase.IN_PROGRESS)
            return RUN_TRANSITION_STATE.RUN_CHANGE_FROM_OOS_NORUN_TO_IN;
          else if (state.getJourneyState().getPhase() == EVehiclePhase.IN_PROGRESS)
            return RUN_TRANSITION_STATE.RUN_CHANGE_FROM_OOS_TO_IN;
          else return RUN_TRANSITION_STATE.RUN_CHANGE_FROM_OOS_TO_OSS;
        } else {
          return RUN_TRANSITION_STATE.RUN_CHANGE_FROM_IS;
        }
      }
    } else {
      if (blockStateObs == null
          && parentState != null
          && parentState.getBlockStateObservation() == null)
        return RUN_TRANSITION_STATE.RUN_CHANGE_FROM_OOS_TO_OSS;
      else return RUN_TRANSITION_STATE.RUN_NOT_CHANGED;
    }
  }
  @Override
  public SensorModelResult likelihood(Context context)
      throws BadProbabilityParticleFilterException {

    final VehicleState state = context.getState();
    final VehicleState parentState = context.getParentState();
    final Observation obs = context.getObservation();
    final EVehiclePhase phase = state.getJourneyState().getPhase();
    final BlockStateObservation blockStateObs = state.getBlockStateObservation();

    //    /*-
    //     * TODO clean up this hack
    //     * We are really in-progress, but because of the
    //     * out-of-service headsign, we can't report it as in-progress
    //     */
    //    if (obs.hasOutOfServiceDsc() && EVehiclePhase.DEADHEAD_DURING == phase
    //        && (blockStateObs != null && blockStateObs.isOnTrip()))
    //      phase = EVehiclePhase.IN_PROGRESS;

    final SensorModelResult result = new SensorModelResult("pEdge", 1d);

    if (obs.getPreviousObservation() == null || parentState == null) {
      if (EVehiclePhase.isActiveDuringBlock(phase)) {
        result.addResultAsAnd("no prev. obs./vehicle-state(in-progress)", 1d);
      } else {
        result.addResultAsAnd("no prev. obs./vehicle-state", 1d);
      }
      return result;
    }

    if (obs.isAtBase()) {
      result.addResultAsAnd("pNotInProgress(base)", 1d);
      return result;
    }

    if (blockStateObs == null) {

      result.addResultAsAnd(computeNoEdgeMovementLogProb(state, obs));
      return result;
    }

    /*
     * Edge Movement
     */
    final boolean previouslyInactive = parentState.getBlockState() == null;
    final boolean newRun =
        MotionModelImpl.hasRunChanged(
            parentState.getBlockStateObservation(), state.getBlockStateObservation());

    final boolean hasMoved = !state.getMotionState().hasVehicleNotMoved();
    if (!previouslyInactive && !newRun) {

      /*
       * This could be a transition from being on a trip geom to off, and vice
       * versa.
       */
      if (EVehiclePhase.IN_PROGRESS != phase) {
        /*
         * We're currently not in-progress
         */
        if (EVehiclePhase.isActiveDuringBlock(phase)) {
          if (state
              .getBlockState()
              .getBlockInstance()
              .equals(parentState.getBlockState().getBlockInstance())) {
            result.addResultAsAnd(
                computeEdgeMovementLogProb(obs, state, parentState, hasMoved, true));
          } else {
            result.addResultAsAnd(computeNoEdgeMovementLogProb(state, parentState, obs));
          }
        } else {
          result.addResultAsAnd(computeNoEdgeMovementLogProb(state, obs));
        }
      } else if (EVehiclePhase.IN_PROGRESS != parentState.getJourneyState().getPhase()) {
        /*
         * We were previously not in-progress, and now we are.
         */
        if (EVehiclePhase.isActiveDuringBlock(parentState.getJourneyState().getPhase())) {
          result.addResultAsAnd(
              computeEdgeMovementLogProb(obs, state, parentState, hasMoved, true));
        } else {
          result.addResultAsAnd(computeNoEdgeMovementLogProb(state, parentState, obs));
        }
      } else {
        /*
         * We're in-progress and were before.
         */
        result.addResultAsAnd(computeEdgeMovementLogProb(obs, state, parentState, hasMoved, false));
      }

    } else {
      /*
       * We have a new run, or just got one after having nothing.
       */

      if (EVehiclePhase.IN_PROGRESS == phase) {
        if (EVehiclePhase.isActiveDuringBlock(parentState.getJourneyState().getPhase())) {
          result.addResultAsAnd(
              computeEdgeMovementLogProb(obs, state, parentState, hasMoved, false));
        } else {
          result.addResultAsAnd(computeNoEdgeMovementLogProb(state, parentState, obs));
        }

      } else {
        result.addResultAsAnd(computeNoEdgeMovementLogProb(state, obs));
      }
    }

    return result;
  }