#############################################################################
# CDMS: Interface to CDMS-simulator
#   (c) 1997/2004: Rolf Schrödter, DLR, Institut für Planetenerkundung
#############################################################################
# $Id: BrowseISB.tcl,v 1.3 2006-11-17 16:34:45+01 rolf Exp rolf $
#############################################################################
# BrowseISB.tcl :  ROLIS Image Status Block interpreter
#############################################################################

namespace eval browse::isb {
    variable ISB

    set ISB(Prev)       {}      ;# Previous ISB frame, interpreted
    set ISB(Next)       {}      ;# Next ISB frame, interpreted; forward searched

    set ISB(FrameSize)  ?       ;# CDMS frame size in bytes
    set ISB(Chan)       ?       ;# CDMS input file
}

#####################################################
# CDMS onboard time
#####################################################
proc browse::isb::obt { data } {

    binary scan $data S1S1S1 v1 v2 v3
    browse::unsigned v1 v2 v3
    ::browse::hk::get_obt $v3 $v2 [expr {$v1 & 0xFF}]
}
proc browse::isb::obt_numeric { data } {

    binary scan $data S1S1S1 v1 v2 v3
    browse::unsigned v1 v2 v3
    ::browse::hk::get_obt_numeric $v3 $v2 [expr {$v1 & 0xFF}]
}
proc browse::isb::rtime { data } {

    binary scan $data S1S1 v1 v2
    browse::unsigned v1 v2
    ::browse::hk::get_time $v1 $v2
}

#############################################################################
# browse::isb::interpret data
#############################################################################
# Return formatted ISB data
#############################################################################
# Parameters:
#   data        - RAW Rolis data frame, big-endian !!!
# Returns:
#   result      - name value name value ... [array get]
#############################################################################
proc browse::isb::interpret {data} {

    binary scan $data @4A8c1c1 isb(Id) isb(BufA) isb(BufB)
    browse::byte isb(BufA) isb(BufB)

    for {set i 0} {$i < 8} {incr i} {
        set pos [expr {16 + $i*30}]

        binary scan $data @${pos}c1c1 isbX(BufNo) iFlags
        browse::byte isbX(BufNo) iFlags

        set flags {}
        foreach {mask name} {0x01 Exposed 0x02 DIS 0x04 Wavelet 0x08 DarkSubtracted 0x10 Full 0x20 Transmitted 0x80 Blocked} {
            if {$iFlags & $mask} {lappend flags $name}
        }
        if {[llength $flags]} {
            set isbX(Flags) [join $flags ","]
        } else {
            set isbX(Flags) "<none>"
        }

        # Evaluate more ISB parameters
        # Note that they have sense only if Flags=Exposed,...
        #
        binary scan $data @${pos}x2a4a6S1S1c1x1S1S1S1S1S1S1 rt lobt exp led pos \
                                isbX(Avrg) isbX(DMA-Err) temp1 isbX(Filter) isbX(ErrCode) temp2
        browse::byte pos
        browse::unsigned exp led temp1 temp2
        browse::unsigned isbX(Avrg) isbX(DMA-Err) isbX(Filter) isbX(ErrCode)

        # All are still binary numbers
        # Format them to show physical values
        #
        set isbX(RTime)    [rtime $rt]
        set isbX(LOBT)     [obt $lobt]
        set isbX(LOBT.Num) [obt_numeric $lobt]
        if {$exp} {
            set x [expr {$exp*3.2}]
            set isbX(ExpTime) [format "%1.0f msec" $x]
        }
        if {$led & 0x8000} {
            set x     [expr {($led >> 8) & 0x03}]
            set color [lindex {RED GREEN BLUE IR} $x]
            set sec   [expr {$led & 0x00FF}]
            set isbX(LED) [format "%s,warmup=%d sec" $color $sec]
        } else {
            set isbX(LED) "<none>"
        }
        set isbX(IFL-Pos) [switch $pos 1 {format "open"} 2 {format "closed"} 3 {format "middle"} default {set pos}]
        if {$temp1} {
            set x [expr {100.0/0.75*3*$temp1/8192.0-273.15}]
            set isbX(CH-Temp1) [format "%+3.2f C" $x]
        } else {
            set isbX(CH-Temp1) "<none>"
        }
        if {$temp2} {
            set x [expr {100.0/0.75*3*$temp2/8192.0-273.15}]
            set isbX(CH-Temp2) [format "%+3.2f C" $x]
        } else {
            set isbX(CH-Temp2) "<none>"
        }

        # Now save the complete current ISB to isb($i)
        #
        set isb($i) [array get isbX]
        unset isbX
    }

    # Return result as [array get]
    #
    array get isb
}

#############################################################################
# browse::isb::findNext
#############################################################################
# Find & interpret next ISB frame
#############################################################################
# Parameters:
#     <none>
# Returns:
#     data  - found ISB raw data, {}=no ISB found
#############################################################################
proc browse::isb::findNext {} {
    variable ISB

    # Remember original file position
    #
    set filePos [tell $ISB(Chan)]

#puts "=== ISB-finder disabled for debugging ==="
puts -nonewline [format "ISB frames Prev=#%d" [expr {[tell $ISB(Chan)] / $ISB(FrameSize)}] ]
#puts ""; return

    set result {}
    while { 1 } {
        set frame [read $ISB(Chan) $ISB(FrameSize)]
        if { [string length $frame] != $ISB(FrameSize) } {
            break
        }
        if { [catch {browse::cdms_frame $frame X} err ] } {
            puts stderr [format "*** %s ***" $err]
            break
        }
        # Interpret data, data is little endian
        #
        binary scan $X(Data) s1 x
        set type [expr {($x >> 8) & 0x00FF}]        ;# Should be = 0x53

        if { $type == 0x53 } {
            decoder::swap X(Data)
            set result $X(Data)
            break
        }

    }
puts [format " Next=#%d" [expr {[tell $ISB(Chan)] / $ISB(FrameSize)}] ]

    # Restore file position & return data
    #
    seek $ISB(Chan) $filePos
    return $result
}


#############################################################################
# browse::isb::found data
#############################################################################
# Find & interpret next ISB frame
#############################################################################
# Parameters:
#     data  - the data frame contents (128 words, big endian)
# Returns:
#     <none>
#############################################################################
proc browse::isb::found {data {search no}} {
    variable ISB

    # Remember this data frame as previous ISB
    #
    set ISB(Prev) [browse::isb::interpret $data]

    if {$search} {
        # Find next ISB & interpret it
        #
        set data [browse::isb::findNext]
        if {[string length $data]} {
            set ISB(Next) [browse::isb::interpret $data]
        } else {
            set ISB(Next) {}
        }
    }
}


#############################################################################
# browse::isb::getISB isbRec img
#############################################################################
# Helper procedure: Get the ISB for img=0..7
#############################################################################
# Parameters:
#     isbRec - One of ISB(Prev) or ISB(Next)
#     img    - Number of image buffer, img=0..7
# Returns:
#     ISB    - ISB: name value name value ... [array get]
#              {} if not found
#############################################################################
proc browse::isb::getISB {isbRec img} {
    if {[llength $isbRec] == 0} {
        return {}
    }
    array set isb $isbRec
    set isb($img)
}

#############################################################################
# browse::isb::isExposed flags
#############################################################################
# Parameters:
#     flags - ISB(Flags), comma-separated list of
# Returns:
#     bool  - 1: Image is exposed or fullframe
#############################################################################
proc browse::isb::isExposed {flags} {
    set flagList [split $flags ","]
    if { [lsearch $flagList "Exposed"] >= 0 } {
        return 1
    } elseif { [lsearch $flagList "Full"   ] >= 0 } {
        return 1
    } else {
        return 0
    }
}

#############################################################################
# browse::isb::checkISB isbRec img lobt
#############################################################################
# Helper procedure checking PREV or NEXT ISB's
#############################################################################
# Parameters:
#     isbRec - One of ISB(Prev) or ISB(Next)
#     img   - Number of image buffer, img=0..7
#     lobt  - Numeric (!) lander onboard time of the first image frame
# Returns:
#     LOBT  - Matching numeric LOBT, or 0 if not matching
#############################################################################
proc browse::isb::checkISB {isbRec img lobt} {
    if {[llength $isbRec] == 0} {
puts [format "ISB check for img=%d lobt=%1.3f <==> <no ISB>" $img $lobt]
        return 0
    }
    array set isbX [getISB $isbRec $img]

puts [format "ISB check img=%d lobt=%1.3f <==> ISB LOBT=%1.3f flags=%s" $img $lobt $isbX(LOBT.Num) [string range $isbX(Flags) 0 11]... ]

    # Validation:
    # 1. The image should be exposed
    # 2. The time of exposure should be earlier than the first telemetry frame
    #
    set exposed [browse::isb::isExposed $isbX(Flags)]
    if { $exposed && ( $isbX(LOBT.Num) < $lobt ) } {
        return $isbX(LOBT.Num)
    } else {
        return 0.0
    }
}

#############################################################################
# browse::isb::getBestISB img lobt
#############################################################################
# Get the best matching ISB for img=0..7 and lander onboard time
# - To be used together with browse::isb::found
#############################################################################
# Parameters:
#     img   - Number of image buffer, img=0..7
#     lobt  - Numeric (!) lander onboard time of the first image frame
# Returns:
#     ISB    - ISB: name value name value ... [array get]
#              {} if not found
#############################################################################
proc browse::isb::getBestISB {img lobt} {
    variable ISB

    # Check & get LOBT from previous & next ISB
    #
    set prev [checkISB $ISB(Prev) $img $lobt]
    set next [checkISB $ISB(Next) $img $lobt]

    # if both ISB's match, then return the latest ISB
    # if PREV or NEXT are matching, return it
    # otherwise return {}
    #
    if { ($prev > 0) && ($next > 0) } {
        if {$next > $prev} {
            set result [getISB $ISB(Next) $img]
            set best $next
        } else {
            set result [getISB $ISB(Prev) $img]
            set best $prev
        }
    } elseif { $next > 0 } {
        set result [getISB $ISB(Next) $img]
        set best $next
    } elseif { $prev > 0 } {
        set result [getISB $ISB(Prev) $img]
        set best $prev
    } else {
        set result {}
        set best 0
    }
puts [format "ISB best=%1.3f" $best]
    set result
}


#############################################################################
# browse::isb::init
#############################################################################
# Initialize ISB Module
#############################################################################
# Parameters:
#     chan      - Input file channel
#     frameSize - CDMS data frame size
# Returns:
#     <none>
#############################################################################
proc browse::isb::init {chan frameSize} {
    variable ISB

    set ISB(Chan)      $chan
    set ISB(FrameSize) $frameSize
}
