HOPS
HOPS class reference
Classes
hops_control.config Namespace Reference

Classes

class  _ConditionBuilder
 
class  Config
 

Detailed Description

Config - accumulates fringe-fitter control statements.

Methods on :class:`Config` are generated at construction time from the JSON
format dictionary supplied by ``MHO_PyControlEvaluator`` (the C++ side passes
the canonical ``control_format`` dict loaded from the installed ``.json`` files).
This keeps the control format defintion in one place (the .json format files), so
we don't need to update C++ and python-code to match each other. All new control 
format definitions should go into:
    '<hops>/source/cpp_src/Control/format/control_extensions'

Control statements are grouped into *conditional blocks* that mirror the 
fourfit control DSL structure. Calls made outside any ``with cfg.IF()`` block
go into the implicit ``if true`` block. Calls inside a ``with`` block go into 
a conditional block whose ``value`` array matches the DSL token 
sequence (e.g. ``["station", "G"]``). Passing this information is crucial for 
downstream operator configuration. 

Boolean conditions are built with a 'fluent builder' object returned 
by ``cfg.IF()``.  That way the builder mirrors the DSL token grammar 
directly, likewise: ``AND()``, ``OR()``, and ``NOT()`` inject boolean 
operators, and the predicate methods (``station()``, ``baseline()``, etc.) 
inject the corresponding operand for subsequent evaluation.

All this results in a syntax which is quite clunky, but the fourfit control 
file DSL is very restrictive (and cannot support a full python-like grammar).
Futhermore, we also need to preserve the boolean conditionals that evaluate 
to 'True' because parameters like 'station E' have to be passed to downstream 
operations for later configuration, for example in order to identify if a 
station-based operation should be applied to the referecne or remote station.

Example synatx:

    def configure(p, cfg):
        cfg.ref_freq(215000.0)  # goes into the implicit if-true block

        # DSL: if station G
        with cfg.IF().station("G"):
            cfg.sampler_delay_x([-140, 180, 180, 180])

        # DSL: if baseline GE
        with cfg.IF().baseline("GE"):
            cfg.ion_npts(11)

        # DSL: if source 3C279 and f_group X
        with cfg.IF().source("3C279").AND().fgroup("X"):
            cfg.ref_freq(86000.0)

        # DSL: if station E and scan > 100-1200
        with cfg.IF().station("E").AND().scan_after("100-1200"):
            cfg.pc_amp_hcode(0.0001)

        # DSL: if station E or station G
        with cfg.IF().station("E").OR().station("G"):
            cfg.weak_channel(0.05)

Convenience shortcuts (``cfg.if_station()``, ``cfg.if_baseline()``, etc.) are
also provided for the common single-predicate case and are equivalent to the 
fluent form, for example:

    with cfg.IF().station("E"):
        cfg.weak_channel(0.05)
        
    is equivalient to:

    with cfg.if_station("E")
        cfg.weak_channel(0.05);

Note! Conditional blocks may not be nested. If multiple predicates are needed,
combine them in one chain with ``AND()``, ``OR()``, and ``NOT()`` rather
than nesting multiple ``with cfg.IF()`` blocks. Nested blocks will raise and exception.