Files
firestar5683 ab81464bd1 Maps
Add navigation and map matching providers with the vendored map service sources.

Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
2026-10-14 19:56:17 -05:00

580 lines
20 KiB
Go

package settings
import (
"encoding/json"
"log/slog"
"os"
"strings"
"time"
"pfeifer.dev/mapd/cereal/custom"
"pfeifer.dev/mapd/cereal/log"
"pfeifer.dev/mapd/params"
"github.com/Jeffail/gabs/v2"
)
const SETTINGS_VERSION = 2 // Used for migrations
var Settings = MapdSettings{
SettingsVersion: SETTINGS_VERSION,
downloadProgress: make(chan DownloadProgress, 1),
cancelDownload: make(chan bool, 1),
}
type SpeedLimitPriority string
const (
PRIORITY_MAP = "map"
PRIORITY_EXTERNAL = "external"
PRIORITY_HIGHEST = "highest"
PRIORITY_LOWEST = "lowest"
)
type MapdSettings struct {
downloadProgress chan DownloadProgress
cancelDownload chan bool
downloadActive bool
externalSpeedLimit float32
speedLimitAccepted bool
currentPersonality log.LongitudinalPersonality
SettingsVersion float32 `json:"settings_version"`
VisionCurveSpeedControlEnabled bool `json:"vision_curve_speed_control_enabled"`
MapCurveSpeedControlEnabled bool `json:"map_curve_speed_control_enabled"`
SpeedLimitControlEnabled bool `json:"speed_limit_control_enabled"`
ExternalSpeedLimitControlEnabled bool `json:"external_speed_limit_control_enabled"`
ConditionalSpeedLimitControlEnabled bool `json:"conditional_speed_limit_control_enabled"`
VisionCurveUseEnableSpeed bool `json:"vision_curve_use_enable_speed"`
SpeedLimitUseEnableSpeed bool `json:"speed_limit_use_enable_speed"`
MapCurveUseEnableSpeed bool `json:"map_curve_use_enable_speed"`
EnableSpeed float32 `json:"enable_speed"`
DefaultLaneWidth float32 `json:"default_lane_width"`
SubscriberSettings SubscriberSettings `json:"subscriber"`
SpeedLimitSettings SpeedLimitSettings `json:"speed_limit"`
LogSettings LogSettings `json:"logger"`
Personalities Personalities `json:"personalities"`
}
type SpeedLimitSettings struct {
PressGasToAcceptSpeedLimit bool `json:"press_gas_to_accept_speed_limit"`
PressGasToOverrideSpeedLimit bool `json:"press_gas_to_override_speed_limit"`
AdjustSetSpeedToAcceptSpeedLimit bool `json:"adjust_set_speed_to_accept_speed_limit"`
AcceptSpeedLimitTimeout float32 `json:"accept_speed_limit_timeout"`
SpeedLimitPriority string `json:"speed_limit_priority"`
SpeedLimitChangeRequiresAccept bool `json:"speed_limit_change_requires_accept"`
HoldLastSeenSpeedLimit bool `json:"hold_last_seen_speed_limit"`
HoldSpeedLimitWhileChangingSetSpeed bool `json:"hold_speed_limit_while_changing_set_speed"`
SpeedLimitOffset float32 `json:"speed_limit_offset"`
}
type LogSettings struct {
LogLevel string `json:"log_level"`
LogJson bool `json:"log_json"`
LogSource bool `json:"log_source"`
}
type Personalities struct {
Relaxed PersonalitySettings `json:"relaxed"`
Standard PersonalitySettings `json:"standard"`
Aggressive PersonalitySettings `json:"aggressive"`
}
type PersonalitySettings struct {
TargetSpeedJerk float32 `json:"target_speed_jerk"`
TargetSpeedAccel float32 `json:"target_speed_accel"`
CurveTargetSpeedTimeOffset float32 `json:"curve_target_speed_time_offset"`
SpeedLimitIncreaseTargetSpeedTimeOffset float32 `json:"speed_limit_increase_target_speed_time_offset"`
SpeedLimitDecreaseTargetSpeedTimeOffset float32 `json:"speed_limit_decrease_target_speed_time_offset"`
MapCurveTargetLatA float32 `json:"map_curve_target_lat_a"`
VisionCurveTargetLatA float32 `json:"vision_curve_target_lat_a"`
VisionCurveMinTargetV float32 `json:"vision_curve_min_target_v"`
SlowDownForNextSpeedLimit bool `json:"slow_down_for_next_speed_limit"`
SpeedUpForNextSpeedLimit bool `json:"speed_up_for_next_speed_limit"`
}
type SubscriberSettings struct {
ShadowCarState bool `json:"shadow_car_state"`
ShadowModelV2 bool `json:"shadow_model_v2"`
ShadowGpsLocation bool `json:"shadow_gps_location"`
ShadowGpsLocationExternal bool `json:"shadow_gps_location_external"`
ShadowSelfdriveState bool `json:"shadow_selfdrive_state"`
}
func (s *MapdSettings) Default() {
err := json.Unmarshal(defaultsJson, s) // load included default settings first to ensure all values have a default
if err != nil {
slog.Warn("failed to load default settings", "error", err)
return
}
// overwrite default values with custom defaults
if _, err := os.Stat("/data/openpilot/mapd_defaults.json"); err == nil {
defaults, err := os.ReadFile("/data/openpilot/mapd_defaults.json")
if err != nil {
slog.Warn("failed to read custom default settings", "error", err)
}
parsed, err := gabs.ParseJSON(defaults)
if err != nil {
slog.Warn("failed to parse custom default settings", "error", err)
}
settingsVersion := parsed.S("settings_version").Data()
if settingsVersion == nil {
settingsVersion = uint64(0)
}
if settingsVersion != SETTINGS_VERSION {
slog.Info("default file settings version mismatch, running migrations", "expected_version", SETTINGS_VERSION, "settings_version", settingsVersion)
migratedSettings := Migrate(uint64(settingsVersion.(uint64)), defaults)
defaults, err = json.Marshal(migratedSettings)
if err != nil {
slog.Warn("failed to marshal migrated default settings", "error", err)
return
}
}
err = json.Unmarshal(defaults, s)
if err != nil {
slog.Warn("failed to load custom default settings", "error", err)
return
}
}
}
// BundledDefaults decodes only the embedded defaults. Diagnostic observation
// must not read device-specific /data overrides or persistent settings.
func BundledDefaults() (MapdSettings, error) {
defaults := MapdSettings{
SettingsVersion: SETTINGS_VERSION,
downloadProgress: make(chan DownloadProgress, 1),
cancelDownload: make(chan bool, 1),
}
err := json.Unmarshal(defaultsJson, &defaults)
return defaults, err
}
func (s *MapdSettings) Recommended() {
if _, err := os.Stat("/data/openpilot/mapd_recommended.json"); err == nil {
recommended, err := os.ReadFile("/data/openpilot/mapd_recommended.json")
if err != nil {
slog.Warn("failed to read custom recommended settings", "error", err)
}
parsed, err := gabs.ParseJSON(recommended)
if err != nil {
slog.Warn("failed to parse custom recommended settings", "error", err)
}
settingsVersion := parsed.S("settings_version").Data()
if settingsVersion == nil {
settingsVersion = uint64(0)
}
if settingsVersion != SETTINGS_VERSION {
slog.Info("default file settings version mismatch, running migrations", "expected_version", SETTINGS_VERSION, "settings_version", settingsVersion)
migratedSettings := Migrate(uint64(settingsVersion.(uint64)), recommended)
recommended, err = json.Marshal(migratedSettings)
if err != nil {
slog.Warn("failed to marshal migrated recommended settings", "error", err)
return
}
}
err = json.Unmarshal(recommended, s)
if err != nil {
slog.Warn("failed to load custom recommended settings", "error", err)
return
}
} else {
err := json.Unmarshal(recommendedJson, s)
if err != nil {
slog.Warn("failed to load recommended settings", "error", err)
}
}
}
func (s *MapdSettings) Load() (success bool) {
data, err := params.GetParam(params.MAPD_SETTINGS)
if err != nil {
slog.Warn("failed to read MAPD_SETTINGS param", "error", err)
return false
}
parsed, err := gabs.ParseJSON(data)
if err != nil {
slog.Warn("failed to parse MAPD_SETTINGS json", "error", err)
return false
}
settingsVersion := parsed.S("settings_version").Data()
if settingsVersion == nil {
settingsVersion = float64(0)
}
if settingsVersion.(float64) != float64(SETTINGS_VERSION) {
slog.Info("settings version mismatch, running migrations", "expected_version", SETTINGS_VERSION, "settings_version", settingsVersion)
migratedSettings := Migrate(uint64(settingsVersion.(float64)), data)
data, err = json.Marshal(migratedSettings)
if err != nil {
slog.Warn("failed to marshal migrated settings", "error", err)
return false
}
}
err = json.Unmarshal(data, s)
if err != nil {
slog.Warn("failed to parse MAPD_SETTINGS param", "error", err)
return false
}
s.setupLogger()
return true
}
func (s *MapdSettings) LoadChanges(data *gabs.Container) (success bool) {
jsonString := data.Bytes()
err := json.Unmarshal(jsonString, s)
if err != nil {
slog.Warn("failed to parse updated settings json", "error", err)
return false
}
s.setupLogger()
return true
}
func (s *MapdSettings) Unmarshal(b []byte) (success bool) {
s.Default() // set defaults so settings not already in param are defaulted
err := json.Unmarshal(b, s)
if err != nil {
slog.Warn("failed to unmarshal settings data", "error", err)
return false
}
s.setupLogger()
return true
}
func (s *MapdSettings) LoadWithRetries(tries int) {
for range tries {
if s.Load() {
break
}
time.Sleep(1 * time.Second)
}
s.Save()
}
func (s *MapdSettings) Save() {
s.SettingsVersion = SETTINGS_VERSION
data, err := json.MarshalIndent(s, "", " ")
if err != nil {
slog.Error("failed to marshal settings to json", "error", err)
return
}
err = params.PutParam(params.MAPD_SETTINGS, data)
if err != nil {
slog.Error("failed to save MAPD_SETTINGS param", "error", err)
return
}
}
func (s *MapdSettings) setupLogger() {
handlerOptions := slog.HandlerOptions{
AddSource: s.LogSettings.LogSource,
}
if s.LogSettings.LogJson {
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &handlerOptions)))
} else {
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &handlerOptions)))
}
s.setLogLevel()
}
func (s *MapdSettings) setLogLevel() {
switch strings.ToLower(s.LogSettings.LogLevel) {
case "debug":
slog.SetLogLoggerLevel(slog.LevelDebug)
case "info":
slog.SetLogLoggerLevel(slog.LevelInfo)
case "warn":
slog.SetLogLoggerLevel(slog.LevelWarn)
case "error":
slog.SetLogLoggerLevel(slog.LevelError)
default:
slog.SetLogLoggerLevel(slog.LevelError)
}
}
func (s *MapdSettings) GetDownloadProgress() (progress DownloadProgress, success bool) {
select {
case progress = <-s.downloadProgress:
s.downloadActive = progress.Active
return progress, true
default:
}
return
}
func (s *MapdSettings) ExternalSpeedLimit() float32 {
return s.externalSpeedLimit
}
func (s *MapdSettings) Handle(input custom.MapdIn) {
switch input.Type() {
case custom.MapdInputType_reloadSettings:
s.Default()
s.Load()
case custom.MapdInputType_saveSettings:
s.SettingsVersion = SETTINGS_VERSION
settingsSnapshot := *s
go settingsSnapshot.Save()
case custom.MapdInputType_loadPersistentSettings:
s.Load()
case custom.MapdInputType_loadDefaultSettings:
s.Default()
case custom.MapdInputType_loadRecommendedSettings:
s.Recommended()
case custom.MapdInputType_cancelDownload:
select {
case s.cancelDownload <- true:
default:
}
case custom.MapdInputType_download:
path, err := input.Str()
if err != nil {
slog.Warn("failed to read download path string", "error", err)
return
}
if !s.downloadActive {
go Download(path, s.downloadProgress, s.cancelDownload)
}
case custom.MapdInputType_acceptSpeedLimit:
s.AcceptSpeedLimit()
case custom.MapdInputType_setJsonPathBool:
s.setSetting(input)
case custom.MapdInputType_setJsonPathFloat:
s.setSetting(input)
case custom.MapdInputType_setJsonPathText:
s.setSetting(input)
// DEPRECATED settings inputs
case custom.MapdInputType_setVisionCurveMinTargetV:
s.Personalities.Aggressive.VisionCurveMinTargetV = input.Float()
s.Personalities.Relaxed.VisionCurveMinTargetV = input.Float()
s.Personalities.Standard.VisionCurveMinTargetV = input.Float()
case custom.MapdInputType_setVisionCurveTargetLatA:
s.Personalities.Aggressive.VisionCurveTargetLatA = input.Float()
s.Personalities.Relaxed.VisionCurveTargetLatA = input.Float()
s.Personalities.Standard.VisionCurveTargetLatA = input.Float()
case custom.MapdInputType_setVisionCurveSpeedControl:
s.VisionCurveSpeedControlEnabled = input.Bool()
case custom.MapdInputType_setSpeedLimitControl:
s.SpeedLimitControlEnabled = input.Bool()
case custom.MapdInputType_setConditionalSpeedLimitControl:
s.ConditionalSpeedLimitControlEnabled = input.Bool()
case custom.MapdInputType_setMapCurveSpeedControl:
s.MapCurveSpeedControlEnabled = input.Bool()
case custom.MapdInputType_setSpeedLimitOffset:
s.SpeedLimitSettings.SpeedLimitOffset = input.Float()
case custom.MapdInputType_setEnableSpeed:
s.EnableSpeed = input.Float()
case custom.MapdInputType_setVisionCurveUseEnableSpeed:
s.VisionCurveUseEnableSpeed = input.Bool()
case custom.MapdInputType_setMapCurveUseEnableSpeed:
s.MapCurveUseEnableSpeed = input.Bool()
case custom.MapdInputType_setSpeedLimitUseEnableSpeed:
s.SpeedLimitUseEnableSpeed = input.Bool()
case custom.MapdInputType_setHoldLastSeenSpeedLimit:
s.SpeedLimitSettings.HoldLastSeenSpeedLimit = input.Bool()
case custom.MapdInputType_setTargetSpeedJerk:
s.Personalities.Aggressive.TargetSpeedJerk = input.Float()
s.Personalities.Relaxed.TargetSpeedJerk = input.Float()
s.Personalities.Standard.TargetSpeedJerk = input.Float()
case custom.MapdInputType_setTargetSpeedAccel:
s.Personalities.Aggressive.TargetSpeedAccel = input.Float()
s.Personalities.Relaxed.TargetSpeedAccel = input.Float()
s.Personalities.Standard.TargetSpeedAccel = input.Float()
case custom.MapdInputType_setTargetSpeedTimeOffset:
s.Personalities.Aggressive.CurveTargetSpeedTimeOffset = input.Float()
s.Personalities.Relaxed.CurveTargetSpeedTimeOffset = input.Float()
s.Personalities.Standard.CurveTargetSpeedTimeOffset = input.Float()
s.Personalities.Aggressive.SpeedLimitIncreaseTargetSpeedTimeOffset = input.Float()
s.Personalities.Relaxed.SpeedLimitIncreaseTargetSpeedTimeOffset = input.Float()
s.Personalities.Standard.SpeedLimitIncreaseTargetSpeedTimeOffset = input.Float()
s.Personalities.Aggressive.SpeedLimitDecreaseTargetSpeedTimeOffset = input.Float()
s.Personalities.Relaxed.SpeedLimitDecreaseTargetSpeedTimeOffset = input.Float()
s.Personalities.Standard.SpeedLimitDecreaseTargetSpeedTimeOffset = input.Float()
case custom.MapdInputType_setDefaultLaneWidth:
s.DefaultLaneWidth = input.Float()
case custom.MapdInputType_setMapCurveTargetLatA:
s.Personalities.Aggressive.MapCurveTargetLatA = input.Float()
s.Personalities.Relaxed.MapCurveTargetLatA = input.Float()
s.Personalities.Standard.MapCurveTargetLatA = input.Float()
case custom.MapdInputType_setExternalSpeedLimitControl:
s.ExternalSpeedLimitControlEnabled = input.Bool()
case custom.MapdInputType_setSpeedLimitPriority:
priority, err := input.Str()
if err != nil {
slog.Warn("failed to read speed limit priority string", "error", err)
return
}
s.SpeedLimitSettings.SpeedLimitPriority = priority
case custom.MapdInputType_setExternalSpeedLimit:
s.externalSpeedLimit = input.Float()
case custom.MapdInputType_setHoldSpeedLimitWhileChangingSetSpeed:
s.SpeedLimitSettings.HoldSpeedLimitWhileChangingSetSpeed = input.Bool()
case custom.MapdInputType_setSpeedUpForNextSpeedLimit:
s.Personalities.Aggressive.SpeedUpForNextSpeedLimit = input.Bool()
s.Personalities.Relaxed.SpeedUpForNextSpeedLimit = input.Bool()
s.Personalities.Standard.SpeedUpForNextSpeedLimit = input.Bool()
case custom.MapdInputType_setSlowDownForNextSpeedLimit:
s.Personalities.Aggressive.SlowDownForNextSpeedLimit = input.Bool()
s.Personalities.Relaxed.SlowDownForNextSpeedLimit = input.Bool()
s.Personalities.Standard.SlowDownForNextSpeedLimit = input.Bool()
case custom.MapdInputType_setSpeedLimitChangeRequiresAccept:
s.SpeedLimitSettings.SpeedLimitChangeRequiresAccept = input.Bool()
case custom.MapdInputType_setPressGasToAcceptSpeedLimit:
s.SpeedLimitSettings.PressGasToAcceptSpeedLimit = input.Bool()
case custom.MapdInputType_setPressGasToOverrideSpeedLimit:
s.SpeedLimitSettings.PressGasToOverrideSpeedLimit = input.Bool()
case custom.MapdInputType_setShadowCarState:
s.SubscriberSettings.ShadowCarState = input.Bool()
case custom.MapdInputType_setShadowModelV2:
s.SubscriberSettings.ShadowModelV2 = input.Bool()
case custom.MapdInputType_setShadowGpsLocation:
s.SubscriberSettings.ShadowGpsLocation = input.Bool()
case custom.MapdInputType_setShadowGpsLocationExternal:
s.SubscriberSettings.ShadowGpsLocationExternal = input.Bool()
case custom.MapdInputType_setAdjustSetSpeedToAcceptSpeedLimit:
s.SpeedLimitSettings.AdjustSetSpeedToAcceptSpeedLimit = input.Bool()
case custom.MapdInputType_setAcceptSpeedLimitTimeout:
s.SpeedLimitSettings.AcceptSpeedLimitTimeout = input.Float()
case custom.MapdInputType_setLogLevel:
logLevel, err := input.Str()
if err != nil {
slog.Warn("failed to read log level string", "error", err)
return
}
s.LogSettings.LogLevel = logLevel
s.setLogLevel()
case custom.MapdInputType_setLogSource:
s.LogSettings.LogSource = input.Bool()
s.setupLogger()
case custom.MapdInputType_setLogJson:
s.LogSettings.LogJson = input.Bool()
s.setupLogger()
}
}
func (s *MapdSettings) PrioritySpeedLimit(mapLimit float32) float32 {
if s.SpeedLimitControlEnabled && !s.ExternalSpeedLimitControlEnabled {
return mapLimit
}
if s.ExternalSpeedLimitControlEnabled && !s.SpeedLimitControlEnabled {
return s.externalSpeedLimit
}
if !s.SpeedLimitControlEnabled && !s.ExternalSpeedLimitControlEnabled {
return 0
}
switch s.SpeedLimitSettings.SpeedLimitPriority {
case PRIORITY_MAP:
if mapLimit == 0 {
return s.externalSpeedLimit
}
return mapLimit
case PRIORITY_EXTERNAL:
if s.externalSpeedLimit == 0 {
return mapLimit
}
return s.externalSpeedLimit
case PRIORITY_HIGHEST:
return max(mapLimit, s.externalSpeedLimit)
case PRIORITY_LOWEST:
if mapLimit == 0 {
return s.externalSpeedLimit
}
if s.externalSpeedLimit == 0 {
return mapLimit
}
return min(mapLimit, s.externalSpeedLimit)
default:
if mapLimit == 0 {
return s.externalSpeedLimit
}
return mapLimit
}
}
func (s *MapdSettings) setSetting(input custom.MapdIn) {
data, err := json.Marshal(s)
if err != nil {
slog.Error("failed to marshal settings to json", "error", err)
return
}
jsonParsed, err := gabs.ParseJSON(data)
if err != nil {
slog.Error("failed to parse settings json", "error", err)
return
}
path, err := input.JsonPath()
if err != nil {
slog.Error("failed to read json path string", "error", err)
return
}
var value any = nil
switch input.Type() {
case custom.MapdInputType_setJsonPathText:
textVal, err := input.Str()
if err != nil {
slog.Error("failed to read text value string", "error", err)
return
}
value = textVal
case custom.MapdInputType_setJsonPathBool:
value = input.Bool()
case custom.MapdInputType_setJsonPathFloat:
value = input.Float()
}
_, err = jsonParsed.SetP(value, path)
if err != nil {
slog.Error("failed to set value at json path", "error", err)
return
}
s.LoadChanges(jsonParsed)
}
func (s *MapdSettings) ResetSpeedLimitAccepted() {
s.speedLimitAccepted = false
}
func (s *MapdSettings) SpeedLimitAccepted() bool {
if !s.SpeedLimitSettings.SpeedLimitChangeRequiresAccept {
return true
}
return s.speedLimitAccepted
}
func (s *MapdSettings) AcceptSpeedLimit() {
s.speedLimitAccepted = true
}
func (s *MapdSettings) SetPersonality(p log.LongitudinalPersonality) {
s.currentPersonality = p
}
func (s *MapdSettings) CurrentPersonality() PersonalitySettings {
switch s.currentPersonality {
case log.LongitudinalPersonality_aggressive:
return s.Personalities.Aggressive
case log.LongitudinalPersonality_relaxed:
return s.Personalities.Relaxed
default:
return s.Personalities.Standard
}
}