////////////////////////////////////////////////////////////////////////
        // Lifecycle
        ////////////////////////////////////////////////////////////////////////

        public ScopeViewModel()
        {
            spec        = Spectrometer.getInstance();
            appSettings = AppSettings.getInstance();

            appSettings.PropertyChanged  += handleAppSettingsChange;
            spec.PropertyChanged         += handleSpectrometerChange;
            spec.showAcquisitionProgress += showAcquisitionProgress; // closure?

            // bind closures (method calls) to each Command
            acquireCmd = new Command(() => { _ = doAcquireAsync(); });
            refreshCmd = new Command(() => { _ = doAcquireAsync(); });
            saveCmd    = new Command(() => { _ = doSave(); });
            addCmd     = new Command(() => { _ = doAdd(); });
            clearCmd   = new Command(() => { _ = doClear(); });

            xAxisOptions = new ObservableCollection <XAxisOption>()
            {
                // these names must match the fields in ChartDataPoint
                new XAxisOption()
                {
                    name = "pixel", unit = "px"
                },
                new XAxisOption()
                {
                    name = "wavelength", unit = "nm"
                },
                new XAxisOption()
                {
                    name = "wavenumber", unit = "cm⁻¹"
                }
            };
            xAxisOption = xAxisOptions[0];

            updateChart();
        }