using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
using Ivi.Driver;
using Teradyne.Instruments.HighSpeed.SubsystemDriver;

namespace Raytheon.GuidedElectronicsUnit
{
    internal class HardwareSession9100 : HardwareSession
        , IGuidedElectronicsUnitLowLevelHSSub9100
        , IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulator
        , IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA
        , IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS
        , IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT
        , IGuidedElectronicsUnitGsKwSyncronousDataLinkControl
        , IGuidedElectronicsUnitDiscrete
        , IGuidedElectronicsUnitInertialMeasurementUnitEmulator
        , IGuidedElectronicsUnitTestControl
    {
        public HardwareSession9100()
        {
            ApplicationConstant = 0x4701;
            ApplicationPrefix = "GEU";
            ChipId = "G1";
            AdcBitCount = 12;

            WordSwap = false;

            ExpectedApplicationId = RegisterMapHSSub9100.ExpectedAppID;
            ExpectedCustomerId = RegisterMapHSSub9100.ExpectedCustID;
            ExpectedRevisionId = RegisterMapHSSub9100.ExpectedRevID;

            RegisterConstant = RegisterMapHSSub9100.TER_LB__FPGA_CONST;
            RegisterApplicationCustomerId = RegisterMapHSSub9100.TER_LB__APP_CUST_ID;
            RegisterRevisionId = RegisterMapHSSub9100.TER_LB__REV_ID;
            RegisterPllStatus = RegisterMapHSSub9100.TER_LB__PLL;
            RegisterVccIntDieTemp = RegisterMapHSSub9100.TER_LB__VCCINT_DT;
            RegisterVccAux = RegisterMapHSSub9100.TER_LB__VCCAUX;

            sdlcMessages = new Queue<RxSdlcMessage>();
        }

        public IGuidedElectronicsUnitLowLevelHSSub9100 LowLevel => this as IGuidedElectronicsUnitLowLevelHSSub9100;
        public IGuidedElectronicsUnitGsKwSyncronousDataLinkControl GsKwSDLC => this as IGuidedElectronicsUnitGsKwSyncronousDataLinkControl;
        public IGuidedElectronicsUnitDiscrete Discrete => this as IGuidedElectronicsUnitDiscrete;
        public IGuidedElectronicsUnitInertialMeasurementUnitEmulator ImuEmulator => this as IGuidedElectronicsUnitInertialMeasurementUnitEmulator;
        public IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulator EdaEmulator => this as IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulator;
        public IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA ChannelA => this as IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA;
        public IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS CS => this as IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS;
        public IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT PT => this as IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT;
        public IGuidedElectronicsUnitTestControl Test => this;

        #region I/O Map

        // PIM0_DP0      | A01, A02     | KW_TO_GS_MC_SDLC_CLK_H/_L        | IN 
        // PIM0_DP1      | B01, B02     | KW_TO_GS_MC_SDLC_DATA_H/_L       | IN 
        // PIM0_DP2      | A04, A05     | GS_MC_TO_KW_SDLC_CLK_H/_L        | OUT
        // PIM0_DP3      | B04, B05     | GS_MC_TO_KW_SDLC_DATA_H/_L       | OUT
        // PIM0_DP4      | A07, A08     | GS_MC_TO_KW_CLK_SYNC_H/_L        | OUT
        // PIM0_DP5      | B07, B08     | (NC) SDLC_ACQ_RX_NC_TX_CLK_H/_L  | IN 
        // PIM0_DP6      | A10, A11     | (NC) SDLC_ACQ_RX_NC_TX_DATA_H/_L | IN 
        // PIM0_DP7      | B10, B11     | (NC) SDLC_NC_RX_ACQ_TX_CLK_H/_L  | IN 
        // PIM0_DP8      | A13, A14     | (NC) SDLC_NC_RX_ACQ_TX_DATA_H/_L | IN 
        // PIM0_DP9      | B13, B14     | KW_TEST_MODE_H/_L                | OUT
        // PIM0_DP10     | A16, A17     | (NC) NC_ACQ_MF_SYNC_H/_L         | IN 
        // PIM0_DP11     | B16, B17     | (NC) NC_ACQ_SF_SYNC_H/_L         | IN 
        // PIM0_DP12     | A19, A20     | NC_DACS_KW_ORD_EN_H/_L           | IN 
        // PIM0_DP13     | B19, B20     | (NC)                             | IN 
        // PIM0_DP14     | A22, A23     | (NC)                             | IN 
        // PIM0_DP15     | B22, B23     | (NC)                             | IN 
        // PIM0_DP16     | D01, D02     | EDA_NC_CLK_H/_L                  | OUT
        // PIM0_DP17     | C01, C02     | EDA_NC_PT_H/_L                   | OUT
        // PIM0_DP18     | D04, D05     | EDA_NC_STAT_H/_L                 | OUT
        // PIM0_DP19     | C04, C05     | NC_EDA_CLK_H/_L                  | IN 
        // PIM0_DP20     | D07, D08     | NC_EDA_CNT_H/_L                  | IN 
        // PIM0_DP21     | C07, C08     | NC_EDA_PT_H/_L                   | IN 
        // PIM0_DP22     | D10, D11     | (NC)                             | IN 
        // PIM0_DP23     | C10, C11     | (NC)                             | IN 
        // PIM0_DP24     | D13, D14     | (NC)                             | IN 
        // PIM0_DP25     | C13, C14     | (NC)                             | IN 
        // PIM0_DP26     | D16, D17     | (NC)                             | IN 
        // PIM0_DP27     | C16, C17     | (NC)                             | IN 
        // PIM0_DP28     | D19, D20     | (NC)                             | IN 
        // PIM0_DP29     | C19, C20     | (NC)                             | IN 
        // PIM0_DP30     | D22, D23     | (NC) NC_IOC_SPARE_1_H/_L         | IN 
        // PIM0_DP31     | C22, C23     | (NC) NC_IOC_SPARE_3_H/_L         | IN 
        //---PIM1------  --------------------------------------------------------------
        // PIM1_DP0      | A01, A02     | IMU_SER_DATA_H/_L                | OUT
        // PIM1_DP1      | B01, B02     | IMU_SER_DATA_CLK_H/_L            | OUT
        // PIM1_DP2      | A04, A05     | IMU_END_DATA_SAMP_H/_L           | OUT
        // PIM1_DP3      | B04, B05     | IMU_EXT_SYNC_H/_L                | IN 
        // PIM1_DP4      | A07, A08     | IMU_INHIBIT_INTERNAL_POR_H/_L    | IN 
        // PIM1_DP5      | B07, B08     | IMU_NOT_INHIBIT_BIT_FAIL_H/_L    | IN 
        // PIM1_DP6      | A10, A11     | IMU_FAST_RESTART_H/_L            | IN 
        // PIM1_DP7      | B10, B11     | IMU_NOT_EXE_COMMAND_BIT_H/_L     | IN 
        // PIM1_DP8      | A13, A14     | IMU_SIM_PWR_ON_RESET_H/_L        | IN 
        // PIM1_DP9      | B13, B14     | IMU_SIM_SER_DATA_H/_L            | OUT
        // PIM1_DP10     | A16, A17     | IMU_SIM_SER_DATA_CLK_H/_L        | OUT
        // PIM1_DP11     | B16, B17     | IMU_SIM_END_DATA_SAMP_H/_L       | OUT
        // PIM1_DP12     | A19, A20     | IMU_SIM_EXT_SYNC_H/_L            | IN 
        // PIM1_DP13     | B19, B20     | IMU_SIM_NOT_INHIBIT_BIT_H/_L     | IN 
        // PIM1_DP14     | A22, A23     | IMU_SIM_FAST_RESTART_H/_L        | IN 
        // PIM1_DP15     | B22, B23     | IMU_SIM_NOT_COMMAND_BIT_H/_L     | IN 
        // PIM1_DP16     | D01, D02     | IMU_PWR_ON_RESET_H/_L            | IN 
        // PIM1_DP17     | C01, C02     | (NC)                             | IN 
        // PIM1_DP18     | D04, D05     | (NC)                             | IN 
        // PIM1_DP19     | C04, C05     | (NC)                             | IN 
        // PIM1_DP20     | D07, D08     | (NC)                             | IN 
        // PIM1_DP21     | C07, C08     | (NC)                             | IN 
        // PIM1_DP22     | D10, D11     | (NC)                             | IN 
        // PIM1_DP23     | C10, C11     | (NC)                             | IN 
        // PIM1_DP24     | D13, D14     | (NC)                             | IN 
        // PIM1_DP25     | C13, C14     | (NC)                             | IN 
        // PIM1_DP26     | D16, D17     | (NC)                             | IN 
        // PIM1_DP27     | C16, C17     | (NC)                             | IN 
        // PIM1_DP28     | D19, D20     | (NC)                             | IN 
        // PIM1_DP29     | C19, C20     | (NC)                             | IN 
        // PIM1_DP30     | D22, D23     | (NC)                             | IN 
        // PIM1_DP31     | C22, C23     | (NC)                             | IN 

        #endregion

        #region Protected Properties

        protected readonly object messageLock = new();
        protected readonly object responseDataLock = new();
        private int pam0;
        private int pam1;
        private ulong pamAddr0;
        private ulong pamAddr1;
        private int pamBufferSize = 1048576;
        private Queue<RxSdlcMessage> sdlcMessages;
        private Queue<byte> gsMcKwSdlcData = new();
        private RxSdlcAutoForwardBuffer autoForwardMessageBuffer = new();
        private uint AutoForwardDestination { get; set; }


        #region Bit Posistions

        private const int NcDacsKwOrdEnBit = 0;

        #endregion

        #endregion

        #region Protected Methods

        protected override void GetInitialDefaults()
        {
            base.GetInitialDefaults();
        }

        protected override void SetDefaults()
        {
            base.SetDefaults();
        }

        #endregion

        #region Background Thread

        internal int pollingInterval;

        internal BackgroundWorker messageChecker;

        internal void SetupBackgroundWorker(int bufferSize, int interval)
        {
            messageChecker = new()
            {
                WorkerSupportsCancellation = true,
            };
            pollingInterval = interval;
            autoForwardMessageBuffer.InitialCapacity = bufferSize;
            pamBufferSize = bufferSize / 2;
            messageChecker.DoWork += new DoWorkEventHandler(CheckForMessages);

            // Allocate PCIe memory and inform FW of the address
            pam0 = TerHsiSession.PAM_Alloc("SDLC Buffer 0", pamBufferSize);
            pam1 = TerHsiSession.PAM_Alloc("SDLC Buffer 1", pamBufferSize);
            pamAddr0 = (ulong)TerHsiSession.PAM_FetchBufferInfo(pam0)[0];
            pamAddr1 = (ulong)TerHsiSession.PAM_FetchBufferInfo(pam1)[0];
            //Console.WriteLine("Pam0 Address (GS_KW__SYS_ADDR_H0): {0:X8}", pamAddr0);
            //Console.WriteLine("Pam1 Address (GS_KW__SYS_ADDR_H1): {0:X8}", pamAddr1);

            byte[] fillData = new byte[pamBufferSize];
            for (int i = 0; i < pamBufferSize; i++)
            {
                fillData[i] = 0xB0;
            }
            TerHsiSession.PAM_Write(pam0, 0, fillData);
            for (int i = 0; i < pamBufferSize; i++)
            {
                fillData[i] = 0xB1;
            }
            TerHsiSession.PAM_Write(pam1, 0, fillData);

            // Update FW with PAM addresses
            TerHsiSession.LB_Write64(RegisterMapHSSub9100.GS_KW__SYS_ADDR_H0, pamAddr0);
            TerHsiSession.LB_Write64(RegisterMapHSSub9100.GS_KW__SYS_ADDR_H1, pamAddr1);

            // Tell FW to start with PAM buffer0.
            TerHsiSession.LB_Write32(RegisterMapHSSub9100.GS_KW__SYS_ADDR_SEL, 0);
            CurrentPam = pam0;

            // Set the auto-forward destination to TAurora
            SetAutoForwardDestination(RegisterMapHSSub9100.DefaultGsKwFwdDestAddr);
            //WriteRegister(RegisterMapHSSub9100.GS_KW__FWD_DEST_ADDR, RegisterMapHSSub9100.DefaultGsKwFwdDestAddr);

            // Enable auto forwarding
            AutoForwardEnabled = (int)(AutoForwardControl.EnableOn | AutoForwardControl.TlpHeaderEnable | AutoForwardControl.SizeEnable);

            // Start the background worker thread
            messageChecker.RunWorkerAsync();
        }

        private void SetAutoForwardDestination(uint destination)
        {
            AutoForwardDestination = destination;

            // Switch Pam Buffer twice to clear counters.
            SwitchPamBuffer();
            SwitchPamBuffer();

            WriteRegister(RegisterMapHSSub9100.GS_KW__FWD_DEST_ADDR, AutoForwardDestination);
        }

        private enum AutoForwardControl
        {
            EnableOn = 0x1,
            TlpHeaderEnable = 0x2,
            SizeEnable = 0x4
        }

        private int AutoForwardEnabled
        {
            get => (int)ReadRegister(RegisterMapHSSub9100.GS_KW__FWD_EN);
            set => ReadModifyWrite(RegisterMapHSSub9100.GS_KW__FWD_EN, (uint)value, 0x7);
        }

        private int CurrentPam { get; set; }

        private void SwitchPamBuffer()
        {
            //uint wordCountRegister = 0;
            //int previousPamIdx = 0;
            //uint previousWordCountRegister = 0;
            var pamIndex = CurrentPam == pam0 ? 1 : 0;
            //switch (pamIndex)
            //{
            //    case 0:
            //        previousPamIdx = 1;
            //        wordCountRegister = RegisterMapHSSub9100.GS_KW__SYS_DW_CNT0;
            //        previousWordCountRegister = RegisterMapHSSub9100.GS_KW__SYS_DW_CNT1;
            //        break;
            //    case 1:
            //        previousPamIdx = 0;
            //        wordCountRegister = RegisterMapHSSub9100.GS_KW__SYS_DW_CNT1;
            //        previousWordCountRegister = RegisterMapHSSub9100.GS_KW__SYS_DW_CNT0;
            //        break;
            //}
            // Inform FW to switch buffers
            WriteRegister(RegisterMapHSSub9100.GS_KW__SYS_ADDR_SEL, (uint)pamIndex);
            // Wait for FW to switch
            BusyBitWait16(RegisterMapHSSub9100.GS_KW__FWD_STATUS, RegisterHalf.Upper, (ushort)pamIndex, 0x1, 500e-3);
            CurrentPam = pamIndex == 0 ? pam0 : pam1;
            //Console.WriteLine("Switched to use PAM {0}", pamIndex);
            //Console.WriteLine("PAM {0} byte count = {1}", pamIndex, ReadRegister(wordCountRegister) * 4);
            //Console.WriteLine("PAM {0} byte count = {1}", previousPamIdx, ReadRegister(previousWordCountRegister) * 4);
        }

        internal void CheckForMessages(object sender, DoWorkEventArgs e)
        {
            try
            {
                var frequency = Stopwatch.Frequency;
                var stopwatch = Stopwatch.StartNew();
                while (!messageChecker.CancellationPending)
                {
                    if (AutoForwardDestination == RegisterMapHSSub9100.DefaultGsKwFwdDestAddr)
                    {
                        lock (componentLock)
                        {
                            // Get current buffer
                            var pamHandle = CurrentPam;
                            var currentPamIdx = CurrentPam == pam0 ? 0 : 1;
                            var newPamIdx = CurrentPam == pam0 ? 1 : 0;
                            var wordCountRegister = currentPamIdx == 0 ? RegisterMapHSSub9100.GS_KW__SYS_DW_CNT0 : RegisterMapHSSub9100.GS_KW__SYS_DW_CNT1;
                            //Console.WriteLine("{0}: Checking for data in PAM {1}...", (double)stopwatch.ElapsedTicks / (double)frequency, currentPamIdx);

                            // Get number of DWords to be read
                            var numDWords = ReadRegister(wordCountRegister);

                            if (numDWords > 0)
                            {
                                // Inform FW to switch buffers
                                SwitchPamBuffer();

                                // Get number of DWords to be read
                                var numBytes = ReadRegister(wordCountRegister) * 4;

                                if (BufferCapacity(numBytes) != AutoForwardBufferCapacity.Full)
                                {
                                    //Console.WriteLine("Retieving {0} bytes from PAM {1}", numBytes, currentPamIdx);
                                    // Read the data
                                    var data = TerHsiSession.PAM_Read(pamHandle, 0, numBytes);

                                    // Process Data
                                    lock (responseDataLock)
                                    {
                                        foreach (byte val in data)
                                        {
                                            gsMcKwSdlcData.Enqueue(val);
                                        }
                                    }
                                    UpdateAutoForwardBuffer(gsMcKwSdlcData.Count);
                                }
                            }
                        }
                    }
                    Thread.Sleep(pollingInterval);
                }
            }
            catch (Exception ex)
            {
                AutoForwardProcessExited?.Invoke(this, new SdlcEventArgs() { Message = ex.Message });
            }
            finally
            {
                TerHsiSession.PAM_Free(pam0);
                TerHsiSession.PAM_Free(pam1);
            }
        }

        private void UpdateAutoForwardBuffer(int bufferCount)
        {
            autoForwardMessageBuffer.HighwaterLevel = bufferCount > autoForwardMessageBuffer.HighwaterLevel ? bufferCount : autoForwardMessageBuffer.HighwaterLevel;
        }

        private AutoForwardBufferCapacity autoForwardBufferCapacity { get; set; } = AutoForwardBufferCapacity.Empty;

        private AutoForwardBufferCapacity BufferCapacity(uint messageSize)
        {
            switch (autoForwardBufferCapacity)
            {
                case AutoForwardBufferCapacity.Empty:
                    if ((autoForwardMessageBuffer.InitialCapacity / 2) < gsMcKwSdlcData.Count)
                    {
                        autoForwardBufferCapacity = AutoForwardBufferCapacity.HalfFull;
                        AutoForwardBufferCapacityWarning?.Invoke(this, new SdlcEventArgs(autoForwardBufferCapacity));
                    }
                    break;
                case AutoForwardBufferCapacity.HalfFull:
                    if (autoForwardMessageBuffer.InitialCapacity < (gsMcKwSdlcData.Count + messageSize))
                    {
                        autoForwardBufferCapacity = AutoForwardBufferCapacity.Full;
                        // Disable autoforwarding
                        WriteRegister(RegisterMapHSSub9100.GS_KW__FWD_EN, (uint)ControlState.Disabled);
                        AutoForwardBufferCapacityWarning?.Invoke(this, new SdlcEventArgs(autoForwardBufferCapacity));
                    }
                    break;
                case AutoForwardBufferCapacity.Full:
                    break;
            }

            return autoForwardBufferCapacity;
        }

        public event EventHandler<SdlcEventArgs> AutoForwardProcessExited;

        public event EventHandler<SdlcEventArgs> AutoForwardBufferCapacityWarning;

        RxSdlcAutoForwardBuffer IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.SdlcAutoForwardBuffer => autoForwardMessageBuffer;

        #endregion

        #region Discrete

        SignalState IGuidedElectronicsUnitDiscrete.NcDacsKwOrdEn
        {
            get
            {
                lock (componentLock)
                {
                    return GetSignalState(ReadRegister(RegisterMapHSSub9100.DACS_DISC_IN), NcDacsKwOrdEnBit);
                }
            }
        }

        #endregion

        #region EdaEmulator

        #region Channel A

        void IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.Configure(EdaConfig config)
        {
            CallLoggingHelper.FunctionStart(GetType(), "Configure", nameof(config), config);

            try
            {
                uint data = (
                    Convert.ToUInt32(config.KwdlLinkUp) << (int)EdaConfig.Bit.KwdlLinkUp |
                    Convert.ToUInt32(config.KwdlState) << (int)EdaConfig.Bit.KwdlState |
                    Convert.ToUInt32(config.KwdlPbitPass) << (int)EdaConfig.Bit.KwdlPbitPass |
                    Convert.ToUInt32(config.KwdlMbitComplete) << (int)EdaConfig.Bit.KwdlMbitComplete |
                    Convert.ToUInt32(config.KwdlMbitPass) << (int)EdaConfig.Bit.KwdlMbitPass |
                    Convert.ToUInt32(config.KwdlRFTemp) << (int)EdaConfig.Bit.KwdlRFTemp |
                    Convert.ToUInt32(config.EdaNumCVClear) << (int)EdaConfig.Bit.EdaNumCVClear |
                    Convert.ToUInt32(config.EdaCVClearPassFail) << (int)EdaConfig.Bit.EdaCVClearPassFail |
                    Convert.ToUInt32(config.EdaCVClearPassFailValid) << (int)EdaConfig.Bit.EdaCVClearPassFailValid |
                    Convert.ToUInt32(config.EdaCVReady) << (int)EdaConfig.Bit.EdaCVReady |
                    Convert.ToUInt32(config.EdaCVReadyValid) << (int)EdaConfig.Bit.EdaCVReadyValid |
                    Convert.ToUInt32(config.EdaPbitPass) << (int)EdaConfig.Bit.EdaPbitPass |
                    Convert.ToUInt32(config.EdaPbitValid) << (int)EdaConfig.Bit.EdaPbitValid
                    );

                WriteRegister(RegisterMapHSSub9100.EDA_A__CFG, data);
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "Configure", null);
            }
        }

        EdaConfig IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.FetchConfiguration()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchConfiguration");

            EdaConfig config = new();

            try
            {
                config = new(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG));
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchConfiguration", config);
            }

            return config;
        }

        CvcwData IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.FetchCVCWData() =>
            new(ReadRegister(RegisterMapHSSub9100.EDA_A__CVCW_RX_DATA));

        byte[] IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.FetchCVData()
        {
            var cvData = new List<byte>();

            var rxCnt = ReadRegister(RegisterMapHSSub9100.EDA_A__CV_RX_CNT);
            for (int i = 0; i < rxCnt; i++)
            {
                var data = ReadRegister(RegisterMapHSSub9100.EDA_A__CV_RX_DATA);
                cvData.Add(Convert.ToByte(data & 0x0000_00FF));
            }

            return cvData.ToArray();
        }

        EdaStatus IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.FetchEdaStatus() =>
            new(ReadRegister(RegisterMapHSSub9100.EDA_A__EDA_STS));

        KwdlStatus IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.FetchKwdlStatus() =>
            new(ReadRegister(RegisterMapHSSub9100.EDA_A__KWDL_STS));

        Scoreboard IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.FetchScoreboard() =>
            new(ReadRegister(RegisterMapHSSub9100.EDA_A__SCOREBOARD));

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaPbitValid
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaPbitValid);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaPbitValid, (uint)EdaConfigure.EdaPbitValid);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaPbitPass
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaPbitPass);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaPbitPass, (uint)EdaConfigure.EdaPbitPass);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaCVReadyValid
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaCVReadyValid);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaCVReadyValid, (uint)EdaConfigure.EdaCVReadyValid);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaCVReady
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaCVReady);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaCVReady, (uint)EdaConfigure.EdaCVReady);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaCVClearPassFailValid
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaCVClearPassFailValid);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaCVClearPassFailValid, (uint)EdaConfigure.EdaCVClearPassFailValid);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaCVClearPassFail
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaCVClearPassFail);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaCVClearPassFail, (uint)EdaConfigure.EdaCVClearPassFail);
        }

        short IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.EdaNumCVClear
        {
            get => (short)((ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.EdaNumCVClear) >> (int)EdaConfig.Bit.EdaNumCVClear);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.EdaNumCVClear, (uint)EdaConfigure.EdaNumCVClear);
        }

        short IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.KwdlRFTemp
        {
            get => (short)((ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.KwdlRFTemp) >> (int)EdaConfig.Bit.KwdlRFTemp);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.KwdlRFTemp, (uint)EdaConfigure.KwdlRFTemp);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.KwdlMbitPass
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.KwdlMbitPass);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.KwdlMbitPass, (uint)EdaConfigure.KwdlMbitPass);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.KwdlMbitComplete
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.KwdlMbitComplete);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.KwdlMbitComplete, (uint)EdaConfigure.KwdlMbitComplete);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.KwdlPbitPass
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.KwdlPbitPass);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.KwdlPbitPass, (uint)EdaConfigure.KwdlPbitPass);
        }

        Raytheon.GuidedElectronicsUnit.KwdlState IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.KwdlState
        {
            get
            {
                var val = (ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.KwdlState) >> (int)EdaConfig.Bit.KwdlState;

                if (val > 4)
                {
                    return KwdlState.Invalid;
                }

                return (KwdlState)val;
            }
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, (uint)value << (int)EdaConfig.Bit.KwdlState, (uint)EdaConfigure.KwdlState);
        }

        bool IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelA.KwdlLinkUp
        {
            get => Convert.ToBoolean(ReadRegister(RegisterMapHSSub9100.EDA_A__CFG) & (uint)EdaConfigure.KwdlLinkUp);
            set => ReadModifyWrite(RegisterMapHSSub9100.EDA_A__CFG, Convert.ToUInt32(value) << (int)EdaConfig.Bit.KwdlLinkUp, (uint)EdaConfigure.KwdlLinkUp);
        }

        #region CS

        void IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS.SdlcControl(SdlcControlBits control, ControlState state) =>
                ReadModifyWrite(RegisterMapHSSub9100.EDA_ACS__CONTROL, state == ControlState.Disabled ? 0x0 : 0xffffffff, (uint)control);

        SdlcStatus IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS.FetchSdlcStatus() =>
                new(ReadRegister(RegisterMapHSSub9100.EDA_ACS__STATUS));

        SdlcFifoWrCount IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS.FifoWrCount =>
            new(ReadRegister(RegisterMapHSSub9100.EDA_ACS__FIFO_WR_CNT));

        RxSdlcMessage[] IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS.ReadAllMessages() =>
            ReadAllMessages(SdlcInterface.EDACS);

        RxSdlcMessage IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS.ReadMessage() =>
            ReadMessage(SdlcInterface.EDACS);

        void IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelACS.SendMessage(uint[] data) =>
            SendMessage(SdlcInterface.EDACS, data);

        #endregion

        #region PT

        void IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT.SdlcControl(SdlcControlBits control, ControlState state) =>
                ReadModifyWrite(RegisterMapHSSub9100.EDA_APT__CONTROL, state == ControlState.Disabled ? 0x0 : 0xffffffff, (uint)control);

        SdlcStatus IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT.FetchSdlcStatus() =>
                new(ReadRegister(RegisterMapHSSub9100.EDA_APT__STATUS));

        SdlcFifoWrCount IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT.FifoWrCount =>
            new(ReadRegister(RegisterMapHSSub9100.EDA_APT__FIFO_WR_CNT));

        RxSdlcMessage[] IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT.ReadAllMessages() =>
            ReadAllMessages(SdlcInterface.EDAPT);

        RxSdlcMessage IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT.ReadMessage() =>
            ReadMessage(SdlcInterface.EDAPT);

        void IGuidedElectronicsUnitEncryptorDecryptorAssemblyEmulatorChannelAPT.SendMessage(uint[] data) =>
            SendMessage(SdlcInterface.EDAPT, data);

        #endregion

        #endregion

        #endregion

        #region GsKwSDLC

        private const int rxMaxWaitTime = 5; // Maximum time to wait for an incomming message in Seconds

        private int RxMessageCount { get => (int)ReadRegister(RegisterMapHSSub9100.GS_KW__MSG_CNT); }

        void IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.SdlcControl(SdlcControlBits control, ControlState state)
        {
            CallLoggingHelper.FunctionStart(GetType(), "SdlcControl", nameof(control), control, nameof(state), state);

            try
            {
                lock (componentLock)
                {
                    uint data = 0x0;
                    if (state == ControlState.Enabled) data = 0xFFFF_FFFF;

                    var mask = (uint)control;
                    ReadModifyWrite(RegisterMapHSSub9100.GS_KW__CONTROL, data, mask);
                }
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "SdlcControl", null);
            }
        }

        SdlcStatus IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.FetchStatus() =>
            new SdlcStatus(ReadRegister(RegisterMapHSSub9100.GS_KW__STATUS));

        SdlcFifoWrCount IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.FifoWrCount =>
            new(ReadRegister(RegisterMapHSSub9100.GS_KW__FIFO_WR_CNT));

        RxSdlcMessage[] IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.ReadAllMessages() =>
            ReadAllMessages(SdlcInterface.GSKW);

        RxSdlcMessage IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.ReadMessage() =>
            ReadMessage(SdlcInterface.GSKW);

        void IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.SendMessage(uint[] data) =>
            SendMessage(SdlcInterface.GSKW, data);

        bool IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.Sync
        {
            get => IsBitSet(ReadRegister(RegisterMapHSSub9100.GS_KW__SYNC), 1);
            set => ReadModifyWrite(RegisterMapHSSub9100.GS_KW__SYNC, Convert.ToUInt32(value), 0x1);
        }

        byte[] IGuidedElectronicsUnitGsKwSyncronousDataLinkControl.FetchMessageData()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchMessageData");

            byte[] data = new byte[0];

            try
            {
                lock (responseDataLock)
                {
                    data = gsMcKwSdlcData.ToArray();
                    gsMcKwSdlcData.Clear();
                    autoForwardBufferCapacity = AutoForwardBufferCapacity.Empty;
                }
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchMessageData", null);
            }

            return data;
        }

        #endregion

        #region ImuEmulator

        void IGuidedElectronicsUnitInertialMeasurementUnitEmulator.Configure(ImuConfigure control, ControlState state)
        {
            CallLoggingHelper.FunctionStart(GetType(), "Configure",
                nameof(control), control,
                nameof(state), state);

            try
            {
                uint newState = 0x0;
                if (state == ControlState.Enabled)
                {
                    newState = 0xFFFFFFFF;
                }

                ReadModifyWrite(RegisterMapHSSub9100.IMU__CONFIG, newState, (uint)control);
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "Configure", null);
            }
        }

        double IGuidedElectronicsUnitInertialMeasurementUnitEmulator.EODSampleWidth
        {
            get => (ReadRegister(RegisterMapHSSub9100.IMU__EODS_WIDTH) & 0x0FFF) * 10e-9;
            set
            {
                uint width = (uint)Math.Round(value / 10e-9, MidpointRounding.AwayFromZero);
                WriteRegister(RegisterMapHSSub9100.IMU__EODS_WIDTH, (uint)width);
            }
        }

        ImuConfig IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FetchConfiguration()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchConfiguration");

            ImuConfig config = new ImuConfig();

            try
            {
                config = new ImuConfig(ReadRegister(RegisterMapHSSub9100.IMU__CONFIG));
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchConfiguratin", null);
            }
            return config;
        }

        ImuMessage IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FetchMessageData()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchMessageData");

            ImuMessage msg = new ImuMessage();

            try
            {
                msg = FetchTxData();
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchMessageData", null);
            }
            return msg;
        }

        ImuDiscreteInput IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FetchDiscreteInput()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchDiscreteInput");

            ImuDiscreteInput data = new ImuDiscreteInput();

            try
            {
                data = new(ReadRegister(RegisterMapHSSub9100.IMU__DISC_IN));
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchDiscreteInput", null);
            }
            return data;
        }

        SdlcStatus IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FetchStatus()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchStatus");

            SdlcStatus sdlcStatus = new();

            try
            {
                sdlcStatus = new(ReadRegister(RegisterMapHSSub9100.IMU__STATUS));
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchStatus", null);
            }

            return sdlcStatus;
        }

        SdlcFifoWrCount IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FifoWrCount =>
            new(ReadRegister(RegisterMapHSSub9100.IMU__FIFO_WR_CNT));

        ControlState IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FrameEnable
        {
            get => IsBitSet(ReadRegister(RegisterMapHSSub9100.IMU__FRM_CTRL), 31) ? ControlState.Enabled : ControlState.Disabled;
            set
            {
                uint mask = 0x8000_0000;
                uint state = 0;

                if (value == ControlState.Enabled)
                {
                    state = mask;
                }

                ReadModifyWrite(RegisterMapHSSub9100.IMU__FRM_CTRL, state, mask);
            }
        }

        double IGuidedElectronicsUnitInertialMeasurementUnitEmulator.FramePeriod
        {
            get => (ReadRegister(RegisterMapHSSub9100.IMU__FRM_CTRL) & 0x0FFF_FFFF) * 10e-9;
            set
            {
                var period = Math.Round(value / 10e-9, MidpointRounding.AwayFromZero);
                ReadModifyWrite(RegisterMapHSSub9100.IMU__FRM_CTRL, (uint)period, 0x0FFF_FFFF);
            }
        }

        RxSdlcMessage[] IGuidedElectronicsUnitInertialMeasurementUnitEmulator.ReadAllMessages() =>
            ReadAllMessages(SdlcInterface.IMU);

        RxSdlcMessage IGuidedElectronicsUnitInertialMeasurementUnitEmulator.ReadMessage() =>
            ReadMessage(SdlcInterface.IMU);

        void IGuidedElectronicsUnitInertialMeasurementUnitEmulator.SdlcControl(SdlcControlBits control, ControlState state)
        {
            lock (componentLock)
            {
                uint data = 0x0;
                if (state == ControlState.Enabled) data = 0xFFFF_FFFF;

                var mask = (uint)control;
                ReadModifyWrite(RegisterMapHSSub9100.IMU__CONTROL, data, mask);
            }
        }

        void IGuidedElectronicsUnitInertialMeasurementUnitEmulator.SendMessage(uint[] data) =>
            SendMessage(SdlcInterface.IMU, data);

        void IGuidedElectronicsUnitInertialMeasurementUnitEmulator.SetDataWord(ImuMessageWord word, uint data)
        {
            lock (componentLock)
            {
                var registerOffset = word switch
                {
                    ImuMessageWord.AngleDeltaX => RegisterMapHSSub9100.IMU__AGL_DLTA_X,
                    ImuMessageWord.AngleDeltaY => RegisterMapHSSub9100.IMU__AGL_DLTA_Y,
                    ImuMessageWord.AngleDeltaZ => RegisterMapHSSub9100.IMU__AGL_DLTA_Z,
                    ImuMessageWord.VelocityDeltaX => RegisterMapHSSub9100.IMU__VEL_DLTA_X,
                    ImuMessageWord.VelocityDeltaY => RegisterMapHSSub9100.IMU__VEL_DLTA_Y,
                    ImuMessageWord.VelocityDeltaZ => RegisterMapHSSub9100.IMU__VEL_DLTA_Z,
                    ImuMessageWord.ModeMuxId => RegisterMapHSSub9100.IMU__MODE_MUX,
                    ImuMessageWord.MuxDataWord => RegisterMapHSSub9100.IMU__MUX_DATA,
                    ImuMessageWord.RawGyroCountX => RegisterMapHSSub9100.IMU__RAW_GCNT_X,
                    ImuMessageWord.RawGyroCountY => RegisterMapHSSub9100.IMU__RAW_GCNT_Y,
                    ImuMessageWord.RawGyroCountZ => RegisterMapHSSub9100.IMU__RAW_GCNT_Z,
                    ImuMessageWord.StatusSummaryWord => RegisterMapHSSub9100.IMU__STS_SUM,
                    _ => throw new ArgumentOutOfRangeException(word.ToString()),
                };

                WriteRegister(registerOffset, data);

                UpdateImuMessageChecksum();
            }
        }

        #region Private Methods

        private ImuMessage FetchTxData()
        {
            CallLoggingHelper.FunctionStart(GetType(), "FetchTxData");

            ImuMessage msg = new ImuMessage();

            try
            {
                msg = new ImuMessage
                {
                    AngleDeltaX = ReadRegister(RegisterMapHSSub9100.IMU__AGL_DLTA_X),
                    AngleDeltaY = ReadRegister(RegisterMapHSSub9100.IMU__AGL_DLTA_Y),
                    AngleDeltaZ = ReadRegister(RegisterMapHSSub9100.IMU__AGL_DLTA_Z),
                    VelocityDeltaX = ReadRegister(RegisterMapHSSub9100.IMU__VEL_DLTA_X),
                    VelocityDeltaY = ReadRegister(RegisterMapHSSub9100.IMU__VEL_DLTA_Y),
                    VelocityDeltaZ = ReadRegister(RegisterMapHSSub9100.IMU__VEL_DLTA_Z),
                    ModeMuxId = ReadRegister(RegisterMapHSSub9100.IMU__MODE_MUX),
                    MuxDataWord = ReadRegister(RegisterMapHSSub9100.IMU__MUX_DATA),
                    RawGyroCountX = ReadRegister(RegisterMapHSSub9100.IMU__RAW_GCNT_X),
                    RawGyroCountY = ReadRegister(RegisterMapHSSub9100.IMU__RAW_GCNT_Y),
                    RawGyroCountZ = ReadRegister(RegisterMapHSSub9100.IMU__RAW_GCNT_Z),
                    StatusSummaryWord = ReadRegister(RegisterMapHSSub9100.IMU__STS_SUM),
                    Checksum = ReadRegister(RegisterMapHSSub9100.IMU__CHECKSUM)
                };
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "FetchTxData", msg);
            }
            return msg;
        }

        private void UpdateImuMessageChecksum()
        {
            var messageData = FetchTxData();

            // Calculate checksum on data and update checksum value before writing to instrument
            uint checksum = ~(messageData.AngleDeltaX +
                messageData.AngleDeltaY +
                messageData.AngleDeltaZ +
                messageData.VelocityDeltaX +
                messageData.VelocityDeltaY +
                messageData.VelocityDeltaZ +
                messageData.ModeMuxId +
                messageData.MuxDataWord +
                messageData.RawGyroCountX +
                messageData.RawGyroCountY +
                messageData.RawGyroCountZ +
                messageData.StatusSummaryWord) & 0xFFFF;

            WriteRegister(RegisterMapHSSub9100.IMU__CHECKSUM, checksum);
        }

        #endregion

        #endregion

        #region LowLevel

        int IGuidedElectronicsUnitLowLevelHSSub9100.Chassis => base.Chassis;

        string IGuidedElectronicsUnitLowLevelHSSub9100.Descriptor => base.Descriptor;

        double IGuidedElectronicsUnitLowLevelHSSub9100.DieTemp => base.DieTemp;

        ControlState IGuidedElectronicsUnitLowLevelHSSub9100.EnableIO
        {
            get => IsBitSet(ReadRegister(RegisterMapHSSub9100.PIM_CTRL), 1) ? ControlState.Disabled : ControlState.Enabled;
            set
            {
                var state = Convert.ToBoolean(value);
                WriteRegister(RegisterMapHSSub9100.PIM_CTRL, (uint)(state ? 0 : 1));
                TerHsiSession.Rs485.SetTermEnable(0, terHsi.Rs485PortAll, state);
                TerHsiSession.Rs485.SetTermEnable(1, terHsi.Rs485PortAll, state);
                TerHsiSession.Rs485.SetRxEnable(0, terHsi.Rs485PortAll, state);
                TerHsiSession.Rs485.SetRxEnable(1, terHsi.Rs485PortAll, state);
            }
        }

        FirmwareInformation IGuidedElectronicsUnitLowLevelHSSub9100.GetFirmwareInfo()
        {
            return GetFirmwareInfo();
        }

        TdfPllStatus IGuidedElectronicsUnitLowLevelHSSub9100.GetPllStatus()
        {
            return GetPllStatus();
        }

        bool IGuidedElectronicsUnitLowLevelHSSub9100.IsSimulated => IsSimulated;

        void IGuidedElectronicsUnitLowLevelHSSub9100.ReadModifyWrite(uint address, uint data, uint mask)
        {
            ReadModifyWrite(address, data, mask);
        }

        uint IGuidedElectronicsUnitLowLevelHSSub9100.ReadRegister(uint address)
        {
            return ReadRegister(address);
        }

        void IGuidedElectronicsUnitLowLevelHSSub9100.Reset()
        {
            Reset();

            if (sdlcMessages.Count > 0)
                sdlcMessages.Clear();
        }

        string IGuidedElectronicsUnitLowLevelHSSub9100.SerialNumber => SerialNumber;

        int IGuidedElectronicsUnitLowLevelHSSub9100.Slot => Slot;

        void IGuidedElectronicsUnitLowLevelHSSub9100.WriteRegister(uint address, uint data)
        {
            if (address == RegisterMapHSSub9100.GS_KW__FWD_DEST_ADDR)
            {
                SetAutoForwardDestination(data);
                return;
            }
            WriteRegister(address, data);
        }

        double IGuidedElectronicsUnitLowLevelHSSub9100.VccAux => VccAux;

        double IGuidedElectronicsUnitLowLevelHSSub9100.VccInt => VccInt;

        AuroraStatus IGuidedElectronicsUnitLowLevelHSSub9100.GetAuroraStatus()
        {
            CallLoggingHelper.FunctionStart(GetType(), "GetAuroraStatus");

            var auroraStatus = new AuroraStatus();

            try
            {
                var registerValue = ReadRegister(RegisterMapHSSub9100.TAURORA__STATUS);
                auroraStatus.SoftError = IsBitSet(registerValue, 7);
                auroraStatus.HardError = IsBitSet(registerValue, 6);
                auroraStatus.LinkUp = IsBitSet(registerValue, 5);
                auroraStatus.ChannelUp = IsBitSet(registerValue, 4);
                auroraStatus.LaneUp = (int)(registerValue & 0xF);
            }
            finally
            {
                CallLoggingHelper.FunctionEnd(GetType(), "GetAuroraStatus", auroraStatus);
            }

            return auroraStatus;
        }

        #endregion

        #region TestControl

        void IGuidedElectronicsUnitTestControl.WaitForMsg(ushort count)
        {
            WaitForMsg(SdlcInterface.IMU, count, 1.0);
        }

        #endregion

    }
}
