Attribute VB_Name = "modParsePacket"
Option Explicit

Public Const USB_DiagDataDelayMS As Long = 2500&

Public ACK_Received As Boolean
Public Timeout_flag As Boolean

Public Type DiagDataType
    ADC_V(11) As Single
    PC5_V(3) As Single
    PC5_PRESENT As Boolean
    PC5_SN As Long
    Firmware As Byte
    FPGA As Byte
    SRAMTestPass As Boolean
    SRAMTestData As Long
    TempOffset As Integer
    strTempRaw As String
    strTempCal As String
    PC5Initialized As Boolean
    PC5DCAL As Single
    IsPosHV As Boolean
    Is8_5VPreAmp As Boolean
    Sup9VOn As Boolean
    PreAmpON As Boolean
    HVOn As Boolean
    TECOn As Boolean
    DiagData(0 To 191) As Byte
End Type

Public Enum DP5_DPP_TYPES
    dppDP5
    dppPX5
    dppDP5G
    dppMCA8000D
End Enum

Public Enum CommType
    commRS232 = 0
    commWinUSB = 1
    commSockets = 2
    commNone = 3
End Enum

Public Const preqProcessNone = &H0&
Public Const preqProcessStatus = &H1&
Public Const preqProcessSpectrum = &H2&
Public Const preqRequestScopeData = &H4&
Public Const preqProcessScopeData = &H8&
Public Const preqProcessTextData = &H10&
Public Const preqProcessScopeDataOverFlow = &H20&
Public Const preqProcessInetSettings = &H40&
Public Const preqProcessDiagData = &H80&
Public Const preqProcessHwDesc = &H100&
Public Const preqProcessCfgRead = &H200&
Public Const preqProcessNetFindRead = &H400&
'...
Public Const preqProcessSCAData = &H2000&
Public Const preqProcessAck = &H4000&
Public Const preqProcessError = &H8000&

Public Type DppStateType
    Interface As CommType
    strPort As String
    ReqProcess As Long
    HaveScopeData As Boolean
    ScopeAutoRearm As Boolean
End Type

Public DppState As DppStateType

Public Sub ParsePacketStatus(ByRef P() As Byte, ByRef PIN As Packet_In)
    Dim X As Integer
    
    Dim CSum As Long
    CSum = 0
    If P(0) = SYNC1_ Then
        If P(1) = SYNC2_ Then
            If P(4) < 128 Then  ' LEN MSB < 128, i.e. LEN < 32768
                PIN.LEN = (P(4) * 256) + P(5)
                'PIN.LEN = TwoByteToLong(P(4), P(5))
                PIN.PID1 = P(2)
                PIN.PID2 = P(3)
                For X = 0 To PIN.LEN + 5    ' add up all the bytes except checksum
                    CSum = CSum + P(X)
                Next X
                CSum = CSum + 256 * CLng(P(PIN.LEN + 6)) + CLng(P(PIN.LEN + 7))
                PIN.CheckSum = CSum
                If (CSum And &HFFFF&) = 0 Then
                    PIN.STATUS = 0      ' packet is OK
                    If PIN.LEN > 0 Then
                        For X = 6 To 5 + PIN.LEN
                            PIN.DATA(X - 6) = P(X)
                        Next X
                        ' call processing routines based on PID?
                    End If
                Else
                    PIN.STATUS = PID2_ACK_CHECKSUM_ERROR    ' checksum error
                End If
            Else
                PIN.STATUS = PID2_ACK_LEN_ERROR        ' length error
            End If
        Else
            PIN.STATUS = PID2_ACK_SYNC_ERROR            ' sync error
        End If
    Else
        PIN.STATUS = PID2_ACK_SYNC_ERROR                ' sync error
    End If
    '    Packet_Received = True  ' flag that packet was received, regardless of errors

End Sub

Public Function PID2_TextToString(strPacketSource As String, ByRef PID2 As Byte) As String
    Select Case PID2

'                    If PIN.LEN > 0 Then ' BAD CMD (PID2=7), BAD PARAM (PID2=5), NO PC5 (PID2=11) errors return offending command string
'                        T$ = ""
'                        For X = 0 To PIN.LEN - 1
'                            T$ = T$ + Chr(PIN.DATA(X))
'                        Next X
'                    End If

        Case PID2_ACK_OK    ' ACK OK
            'PID2_TextToString = strPacketSource & ": OK" & vbTab
            PID2_TextToString = ""
            ACK_Received = True
        Case PID2_ACK_SYNC_ERROR
            PID2_TextToString = strPacketSource & ": Sync Error" & vbTab
        Case PID2_ACK_PID_ERROR
            PID2_TextToString = strPacketSource & ": PID Error" & vbTab
        Case PID2_ACK_LEN_ERROR
            PID2_TextToString = strPacketSource & ": Length Error" & vbTab
        Case PID2_ACK_CHECKSUM_ERROR
            PID2_TextToString = strPacketSource & ": Checksum Error" & vbTab
        Case PID2_ACK_BAD_PARAM
            PID2_TextToString = strPacketSource & ": Bad Parameter" & vbTab
            '                            TextLog "ACK: BAD PARAM '" + T$ + "'" + vbCrLf

            
        Case PID2_ACK_BAD_HEX_REC
            PID2_TextToString = strPacketSource & ": Bad HEX Record" & vbTab
            '
'                        Case 7
'                            TextLog "ACK: BAD COMMAND '" + T$ + "'" + vbCrLf
'                        Case 8
'                            TextLog "ACK: FPGA NOT INIT'D" + vbCrLf
'                        Case 9
'                            TextLog "ACK: CP2201 NOT FOUND" + vbCrLf
'                        Case 10
'                            TextLog "ACK: NO SCOPE DATA" + vbCrLf
'                        Case 11
'                            TextLog "ACK: PC5 NOT DETECTED '" + T$ + "'" + vbCrLf
'                       case PID2_ACK_ETHERNET_BUSY
'

            
        Case PID2_ACK_UNRECOG
            PID2_TextToString = strPacketSource & ": Unrecognized Command" & vbTab
    End Select
End Function

Public Function ParsePacket(ByRef P() As Byte, ByRef PIN As Packet_In) As Long
    ParsePacket = preqProcessNone
    ParsePacketStatus P, PIN
    If (PIN.STATUS = PID2_ACK_OK) Then ' no errors
        If ((PIN.PID1 = PID1_RCV_STATUS) And (PIN.PID2 = PID2_SEND_DP4_STYLE_STATUS)) Then ' DP4-style status
            ParsePacket = preqProcessStatus
        ElseIf ((PIN.PID1 = PID1_RCV_SPECTRUM) And ((PIN.PID2 >= RCVPT_256_CHANNEL_SPECTRUM) And (PIN.PID2 <= RCVPT_8192_CHANNEL_SPECTRUM_STATUS))) Then ' spectrum / spectrum+status
            ParsePacket = preqProcessSpectrum
        'ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = PID2_SEND_SCOPE_DATA)) Then'scope data packet
        '    ParsePacket = preqProcessScopeData
        'ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = PID2_SEND_512_BYTE_MISC_DATA)) Then 'text data packet
        '    ParsePacket = preqProcessTextData
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_SCOPE_DATA)) Then 'scope data packet
            'ScopeOverFlow = FALSE
            ParsePacket = preqProcessScopeData
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_512_BYTE_MISC_DATA)) Then 'text data packet
            ParsePacket = preqProcessTextData
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_SCOPE_DATA_WITH_OVERFLOW)) Then 'scope data with overflow packet
            'ScopeOverFlow = TRUE
            ParsePacket = preqProcessScopeData
            'ParsePacket = preqProcessScopeDataOverFlow
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_ETHERNET_SETTINGS)) Then 'ethernet settings packet
            ParsePacket = preqProcessInetSettings
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_DIAGNOSTIC_DATA)) Then 'diagnostic data  packet
            ParsePacket = preqProcessDiagData
        'ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_HARDWARE_DESCRIPTION)) Then 'hardware description packet
            'ParsePacket = preqProcessHwDesc
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_CONFIG_READBACK)) Then
            ParsePacket = preqProcessCfgRead
        ElseIf ((PIN.PID1 = PID1_RCV_SCOPE_MISC) And (PIN.PID2 = RCVPT_NETFINDER_READBACK)) Then
            ParsePacket = preqProcessNetFindRead
            'MsgBox "NetFinder!"
        ElseIf (PIN.PID1 = PID1_ACK) Then
            ParsePacket = preqProcessAck
        Else
            PIN.STATUS = PID2_ACK_PID_ERROR  ' unknown PID
            ParsePacket = preqProcessError
        End If
    Else
        ParsePacket = preqProcessError
    End If

    'PIN.PID1 = 0    ' overwrite the PIDs so packet can't be erroneously processed again
    'PIN.PID2 = 0

End Function

Public Sub Process_Status(STATUS As Stat)
    STATUS.DEVICE_ID = STATUS.RAW(39)       '0=dp5,1=px5,2=dp5g
    Select Case STATUS.DEVICE_ID
        Case 0
            STATUS.strDeviceID = "DP5"
        Case 1
            STATUS.strDeviceID = "PX5"
        Case 2
            STATUS.strDeviceID = "DP5G"
        Case 3
            STATUS.strDeviceID = "MCA8000D"
        Case Else
            STATUS.strDeviceID = "DP5" 'default
    End Select

    STATUS.FAST_COUNT = STATUS.RAW(0) + STATUS.RAW(1) * 256! + STATUS.RAW(2) * 65536! + STATUS.RAW(3) * 1.677722E+07!
    STATUS.SLOW_COUNT = STATUS.RAW(4) + STATUS.RAW(5) * 256! + STATUS.RAW(6) * 65536! + STATUS.RAW(7) * 1.677722E+07!
    STATUS.GP_COUNTER = STATUS.RAW(8) + STATUS.RAW(9) * 256! + STATUS.RAW(10) * 65536! + STATUS.RAW(11) * 1.677722E+07!
    STATUS.ACC_TIME = STATUS.RAW(12) * 0.001 + (STATUS.RAW(13) + STATUS.RAW(14) * 256! + STATUS.RAW(15) * 65536!) * 0.1
    STATUS.REALTIME = (STATUS.RAW(20) + STATUS.RAW(21) * 256! + STATUS.RAW(22) * 65536! + STATUS.RAW(23) * 1.677722E+07!) * 0.001

    STATUS.FW = STATUS.RAW(24)
    STATUS.FP = STATUS.RAW(25)
    
    If STATUS.FW > &H65 Then
        STATUS.BUILD = STATUS.RAW(37) And 15    ' Build # added in FW6.06
    End If
        
    If STATUS.RAW(29) < 128 Then ' avoid overflow since VB long is signed :(
        STATUS.SN = CLng(STATUS.RAW(26)) + CLng(STATUS.RAW(27)) * 256& + CLng(STATUS.RAW(28)) * 65536 + CLng(STATUS.RAW(29)) * 16777216
    Else
        STATUS.SN = -1
    End If
    
    If STATUS.RAW(30) < 128 Then    ' not negative
        STATUS.HV = CSng(STATUS.RAW(31) + (STATUS.RAW(30) * 256)) * 0.5  ' 0.5V/count
    Else
        STATUS.HV = ((CSng(STATUS.RAW(31)) + (CSng(STATUS.RAW(30)) * 256)) - 65536!) * 0.5 ' 0.5V/count
    End If
    
    STATUS.DET_TEMP = CSng(STATUS.RAW(33) + (STATUS.RAW(32) And 15) * 256) * 0.1 - 273.16 ' 0.1K/count
    STATUS.DP5_TEMP = STATUS.RAW(34) - ((STATUS.RAW(34) And 128) * 2)
    
    STATUS.AFAST_LOCKED = ((STATUS.RAW(35) And 64) = 64)
    STATUS.MCA_EN = ((STATUS.RAW(35) And 32) = 32)
    STATUS.PRESET_COUNT_REACHED = ((STATUS.RAW(35) And 16) = 16)
    STATUS.PRESET_REALTIME_REACHED = ((STATUS.RAW(35) And 128) = 128)
    STATUS.SCOPE_DR = ((STATUS.RAW(35) And 4) = 4)
    STATUS.DP5_CONFIGURED = ((STATUS.RAW(35) And 2) = 2)
    
    STATUS.AOFFSET_LOCKED = ((STATUS.RAW(36) And 128) = 0)  ' 0=locked, 1=searching
    STATUS.MCS_DONE = ((STATUS.RAW(36) And 64) = 64)
    
    STATUS.PC5_PRESENT = ((STATUS.RAW(38) And 128) = 128)
    If STATUS.PC5_PRESENT Then
        STATUS.PC5_HV_POL = ((STATUS.RAW(38) And 64) = 64)
        STATUS.PC5_8_5V = ((STATUS.RAW(38) And 32) = 32)
    Else
        STATUS.PC5_HV_POL = False
        STATUS.PC5_8_5V = False
    End If
    
    If STATUS.FW >= &H65 Then   ' reboot flag added FW6.05
        If (STATUS.RAW(36) And 32) = 32 Then
            STATUS.REBOOT_FLAG = True
        Else
            STATUS.REBOOT_FLAG = False
        End If
    Else
        STATUS.REBOOT_FLAG = False
    End If
    
    STATUS.DPP_options = (STATUS.RAW(42) And 15)
    If STATUS.DPP_options > 0 Then
        STATUS.HPGe_HV_INH = ((STATUS.RAW(42) And 32) = 32)
        STATUS.HPGe_HV_INH_POL = ((STATUS.RAW(42) And 16) = 16)
    End If

End Sub

Sub Process_Diagnostics(PIN As Packet_In, ByRef dd As DiagDataType)
    Dim idxVal As Long
    Dim strVal As String
    Dim DP5_ADC_Gain(10) As Single  ' convert each ADC count to engineering units - values calculated in FORM.LOAD
    Dim PC5_ADC_Gain(3) As Single
    Dim PX5_ADC_Gain(11) As Single
    
    DP5_ADC_Gain(0) = 1# / 0.00286              ' 2.86mV/C
    DP5_ADC_Gain(1) = 1#                        ' Vdd mon (out-of-scale)
    DP5_ADC_Gain(2) = (30.1 + 20#) / 20#        ' PWR
    DP5_ADC_Gain(3) = (13# + 20#) / 20#         ' 3.3V
    DP5_ADC_Gain(4) = (4.99 + 20#) / 20#        ' 2.5V
    DP5_ADC_Gain(5) = 1#                        ' 1.2V
    DP5_ADC_Gain(6) = (35.7 + 20#) / 20#        ' 5.5V
    DP5_ADC_Gain(7) = (35.7 + 75#) / 35.7       ' -5.5V (this one is tricky)
    DP5_ADC_Gain(8) = 1#                        ' AN_IN
    DP5_ADC_Gain(9) = 1#                        ' VREF_IN
    
    PC5_ADC_Gain(0) = 500#                      ' HV: 1500V/3V
    PC5_ADC_Gain(1) = 100#                      ' TEC: 300K/3V
    PC5_ADC_Gain(2) = (20# + 10#) / 10#         ' +8.5/5V

    'PX5_ADC_Gain(0) = (30.1 + 20#) / 20#       ' PWR
    PX5_ADC_Gain(0) = (69.8 + 20#) / 20#        ' 9V (was originally PWR)
    PX5_ADC_Gain(1) = (13# + 20#) / 20#         ' 3.3V
    PX5_ADC_Gain(2) = (4.99 + 20#) / 20#        ' 2.5V
    PX5_ADC_Gain(3) = 1#                        ' 1.2V
    PX5_ADC_Gain(4) = (30.1 + 20#) / 20#        ' 5V
    PX5_ADC_Gain(5) = (10.7 + 75#) / 10.7       ' -5V (this one is tricky)
    PX5_ADC_Gain(6) = (64.9 + 20#) / 20#        ' +PA
    PX5_ADC_Gain(7) = (10.7 + 75#) / 10.7       ' -PA
    PX5_ADC_Gain(8) = (16# + 20#) / 20#         ' +TEC
    PX5_ADC_Gain(9) = 500#                      ' HV: 1500V/3V
    PX5_ADC_Gain(10) = 100#                     ' TEC: 300K/3V
    PX5_ADC_Gain(11) = 1# / 0.00286             ' 2.86mV/C

    dd.Firmware = PIN.DATA(0)
    dd.FPGA = PIN.DATA(1)
    strVal = FmtHex(PIN.DATA(2), 2) & FmtHex(PIN.DATA(3), 2) & FmtHex(PIN.DATA(4), 2)
    dd.SRAMTestData = CLng("&H" & strVal)
    dd.SRAMTestPass = CBool(dd.SRAMTestData = &HFFFFFF)
    dd.TempOffset = PIN.DATA(180) + 256 * (PIN.DATA(180) > 127)  ' 8-bit signed value
    If (STATUS.DEVICE_ID = dppDP5) Then
        For idxVal = 0 To 9
            dd.ADC_V(idxVal) = (((PIN.DATA(5 + idxVal * 2) And 3) * 256) + PIN.DATA(6 + idxVal * 2)) * 2.44 / 1024 * DP5_ADC_Gain(idxVal) ' convert counts to engineering units (C or V)
        Next
        dd.ADC_V(7) = dd.ADC_V(7) + dd.ADC_V(6) * (1 - DP5_ADC_Gain(7)) ' -5.5V is a function of +5.5V
        dd.strTempRaw = Format(dd.ADC_V(0) - 271.3, "###.0C")
        dd.strTempCal = Format(dd.ADC_V(0) - 280 + dd.TempOffset, "###.0C")
    ElseIf (STATUS.DEVICE_ID = dppPX5) Then
        For idxVal = 0 To 10
            dd.ADC_V(idxVal) = (((PIN.DATA(5 + idxVal * 2) And 15) * 256) + PIN.DATA(6 + idxVal * 2)) * 3 / 4096 * PX5_ADC_Gain(idxVal)   ' convert counts to engineering units (C or V)
        Next
        dd.ADC_V(11) = (((PIN.DATA(5 + 11 * 2) And 3) * 256) + PIN.DATA(6 + 11 * 2)) * 3 / 1024 * PX5_ADC_Gain(11)  ' convert counts to engineering units (C or V)
        dd.ADC_V(5) = dd.ADC_V(5) - (3 * PX5_ADC_Gain(5)) + 3 ' -5V uses +3VR
        dd.ADC_V(7) = dd.ADC_V(7) - (3 * PX5_ADC_Gain(7)) + 3 ' -PA uses +3VR
        dd.strTempRaw = Format(dd.ADC_V(11) - 271.3, "###.0C")
        dd.strTempCal = Format(dd.ADC_V(11) - 280 + dd.TempOffset, "###.0C")
    End If
    
    dd.PC5_PRESENT = False  ' assume no PC5, then check to see if there are any non-zero bytes
    For idxVal = 25 To 38
        If PIN.DATA(idxVal) > 0 Then
            dd.PC5_PRESENT = True
            Exit For
        End If
    Next

    If dd.PC5_PRESENT Then
        For idxVal = 0 To 2
            dd.PC5_V(idxVal) = (((PIN.DATA(25 + idxVal * 2) And 15) * 256) + PIN.DATA(26 + idxVal * 2)) * 3 / 4096 * PC5_ADC_Gain(idxVal) ' convert counts to engineering units (C or V)
        Next
        If PIN.DATA(34) < 128 Then
            dd.PC5_SN = CLng(PIN.DATA(31)) + CLng(PIN.DATA(32)) * 256 + CLng(PIN.DATA(33)) * (256 ^ 2) + CLng(PIN.DATA(34)) * (256 ^ 3)
        Else
            dd.PC5_SN = -1 ' no PC5 S/N
        End If
        If (PIN.DATA(35) = 255) And (PIN.DATA(36) = 255) Then
            dd.PC5Initialized = False
            dd.PC5DCAL = 0
        Else
            dd.PC5Initialized = True
            dd.PC5DCAL = CSng((CSng(PIN.DATA(35)) * 256 + CSng(PIN.DATA(36))) * 3 / 4096)
        End If
        dd.IsPosHV = CBool(PIN.DATA(37) And 128)
        dd.Is8_5VPreAmp = CBool(PIN.DATA(37) And 64)
        dd.Sup9VOn = CBool(PIN.DATA(38) And 8)
        dd.PreAmpON = CBool(PIN.DATA(38) And 4)
        dd.HVOn = CBool(PIN.DATA(38) And 2)
        dd.TECOn = CBool(PIN.DATA(38) And 1)
    Else
        For idxVal = 0 To 2
            dd.PC5_V(idxVal) = 0
        Next
        dd.PC5_SN = -1 ' no PC5 S/N
        dd.PC5Initialized = False
        dd.PC5DCAL = 0
        dd.IsPosHV = False
        dd.Is8_5VPreAmp = False
        dd.Sup9VOn = False
        dd.PreAmpON = False
        dd.HVOn = False
        dd.TECOn = False
    End If
    For idxVal = 0 To 191
        dd.DiagData(idxVal) = PIN.DATA(idxVal + 39)
    Next idxVal
End Sub

Public Function DiagnosticsToString(dd As DiagDataType) As String
    Dim idxVal As Long
    Dim strDiag As String
    
    strDiag = "Firmware: " & VersionToStr(dd.Firmware) & vbNewLine
    strDiag = strDiag & "FPGA: " & VersionToStr(dd.FPGA) & vbNewLine
    strDiag = strDiag & "SRAM Test: "
    If dd.SRAMTestPass Then
        strDiag = strDiag & "PASS" & vbNewLine
    Else
        strDiag = strDiag & "ERROR @ 0x" & FmtHex(dd.SRAMTestData, 6) & vbNewLine
    End If
    
    If (STATUS.DEVICE_ID = dppDP5) Then
        strDiag = strDiag & "DP5 Temp (raw): " & dd.strTempRaw & vbNewLine
        strDiag = strDiag & "DP5 Temp (cal'd): " & dd.strTempCal & vbNewLine
        strDiag = strDiag & "PWR: " & Format(dd.ADC_V(2), "#.##0V") & vbNewLine
        strDiag = strDiag & "3.3V: " & Format(dd.ADC_V(3), "#.##0V") & vbNewLine
        strDiag = strDiag & "2.5V: " & Format(dd.ADC_V(4), "#.##0V") & vbNewLine
        strDiag = strDiag & "1.2V: " & Format(dd.ADC_V(5), "#.##0V") & vbNewLine
        strDiag = strDiag & "+5.5V: " & Format(dd.ADC_V(6), "#.##0V") & vbNewLine
        strDiag = strDiag & "-5.5V: " & Format(dd.ADC_V(7), "#.##0V") & vbNewLine
        strDiag = strDiag & "AN_IN: " & Format(dd.ADC_V(8), "#.##0V") & vbNewLine
        strDiag = strDiag & "VREF_IN: " & Format(dd.ADC_V(9), "#.##0V") & vbNewLine
        
        strDiag = strDiag & vbNewLine
        If dd.PC5_PRESENT Then
            strDiag = strDiag & "PC5: Present" & vbNewLine
            strDiag = strDiag & "HV: " & Format(dd.PC5_V(0), "####V") & vbNewLine
            strDiag = strDiag & "Detector Temp: " & Format(dd.PC5_V(1), "###.#K") & vbNewLine
            strDiag = strDiag & "+8.5/5V: " & Format(dd.PC5_V(2), "#.##0V") & vbNewLine
            If (dd.PC5_SN > -1) Then
                strDiag = strDiag & "PC5 S/N:" + Str(dd.PC5_SN) & vbNewLine
            Else
                strDiag = strDiag & "PC5 S/N: none" & vbNewLine
            End If
            If (dd.PC5Initialized) Then
                strDiag = strDiag & "PC5 DCAL: " & Format(dd.PC5DCAL, "#.##0V") & vbNewLine
            Else
                strDiag = strDiag & "PC5 DCAL: Uninitialized" & vbNewLine
            End If
            strDiag = strDiag & "PC5 Flavor: "
            strDiag = strDiag & IsAorB(dd.IsPosHV, "+HV, ", "-HV, ")
            strDiag = strDiag & IsAorB(dd.Is8_5VPreAmp, "8.5V preamp", "5V preamp") & vbNewLine
            strDiag = strDiag & "9V: " & OnOffStr(dd.Sup9VOn) & vbNewLine
            strDiag = strDiag & "Preamp: " & OnOffStr(dd.PreAmpON) & vbNewLine
            strDiag = strDiag & "HV: " & OnOffStr(dd.HVOn) & vbNewLine
            strDiag = strDiag & "TEC: " & OnOffStr(dd.TECOn) & vbNewLine
        Else
            strDiag = strDiag & "PC5: Not Present" & vbNewLine
        End If
        
    ElseIf (STATUS.DEVICE_ID = dppPX5) Then
        strDiag = strDiag + "PX5 Temp (raw): " + dd.strTempRaw & vbNewLine
        strDiag = strDiag + "PX5 Temp (cal'd): " + dd.strTempCal & vbNewLine
        'strDiag = strDiag + "PWR: " + Format(dd.ADC_V(0), "#.##0V") & vbNewLine
        strDiag = strDiag + "9V: " + Format(dd.ADC_V(0), "#.##0V") & vbNewLine
        strDiag = strDiag + "3.3V: " + Format(dd.ADC_V(1), "#.##0V") & vbNewLine
        strDiag = strDiag + "2.5V: " + Format(dd.ADC_V(2), "#.##0V") & vbNewLine
        strDiag = strDiag + "1.2V: " + Format(dd.ADC_V(3), "#.##0V") & vbNewLine
        strDiag = strDiag + "+5V: " + Format(dd.ADC_V(4), "#.##0V") & vbNewLine
        strDiag = strDiag + "-5V: " + Format(dd.ADC_V(5), "#.##0V") & vbNewLine
        strDiag = strDiag + "+PA: " + Format(dd.ADC_V(6), "#.##0V") & vbNewLine
        strDiag = strDiag + "-PA: " + Format(dd.ADC_V(7), "#.##0V") & vbNewLine
        strDiag = strDiag + "TEC: " + Format(dd.ADC_V(8), "#.##0V") & vbNewLine
        strDiag = strDiag + "ABS(HV): " + Format(dd.ADC_V(9), "###0.0V") & vbNewLine
        strDiag = strDiag + "DET_TEMP: " + Format(dd.ADC_V(10), "##0.0K") & vbNewLine
    End If
    

    strDiag = strDiag & vbNewLine & "Diagnostic Data" & vbNewLine
    strDiag = strDiag & "---------------" & vbNewLine
    For idxVal = 0 To 191
        If idxVal Mod 8 = 0 Then strDiag = strDiag & FmtHex(idxVal, 2) & ":"
        strDiag = strDiag & FmtHex(dd.DiagData(idxVal), 2) + " "
        If idxVal Mod 8 = 7 Then strDiag = strDiag & vbNewLine
    Next
    DiagnosticsToString = strDiag
End Function

Public Function FmtHex(ByVal DecNum As Long, ByVal HexDig As Long) As String
    Dim strHex As String
    Dim lHex As Long

    strHex = Hex(DecNum)
    lHex = Len(strHex)
    If (lHex < HexDig) Then
        FmtHex = String(HexDig - lHex, "0") + strHex
    Else
        FmtHex = strHex
    End If
End Function

Public Function VersionToStr(bVersion As Byte) As String
    VersionToStr = Trim(Str((bVersion And &HF0) / 16)) + "." + Format((bVersion And &HF), "00")
End Function

Public Function OnOffStr(bOnOff As Boolean) As String
    If (bOnOff) Then
        OnOffStr = "ON"
    Else
        OnOffStr = "OFF"
    End If
End Function

Public Function IsAorB(bIsA As Boolean, strA As String, strB As String) As String
    If (bIsA) Then
        IsAorB = strA
    Else
        IsAorB = strB
    End If
End Function














