Source code for ska_telmodel.csp.schema

"""
Used for checking CSP configuration strings for conformance
"""

from inspect import cleandoc
from itertools import repeat

from schema import And, Optional, Or, Regex, Schema

from .._common import (
    MID_MKT,
    MID_SKA,
    TMSchema,
    mk_if,
    split_interface_version,
)
from . import validators as validators
from . import version as csp_version
from .common_schema import (
    CHANNEL_WIDTH_VALUES,
    IPV4_REGEX_PATTERN,
    MAX_CHANNELS_PER_FSP,
    MAX_CORR_CHANNELS,
    MAX_PORT_VALUE,
    MAX_SIGNED_32BIT_INT,
    _get_common_config_schema_without_band,
    add_mid_frequency_band,
    use_camel_case,
)
from .mid_low_schema import (
    get_csp_pss_assign_release_resources,
    get_csp_pss_configure_schema,
    get_csp_pst_assign_release_resources,
    get_csp_pst_config_schema,
)


[docs] def get_vlbi_config_schema(version: str, strict: bool): """VLBI specific items :param version: Interface Version URI :param strict: Schema strictness :return: the JSON schema for the Mid.CBF VLBI configuration. """ return TMSchema.new( "VLBI config", version, strict, schema={Optional("dummy_param"): str}, description=cleandoc( """ Very Long Baseline Interferometry specific parameters. To be borrowed from IICD This section contains the parameters relevant only for VLBI. This section is forwarded only to CSP subelement. """ ), as_reference=True, )
[docs] def get_fsp_config_schema(version: str, strict: bool): """Frequency slice processor configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON schema for the MID.CBF FSP configuration. """ if_strict = mk_if(strict) camel_case = use_camel_case(version) (major, minor) = split_interface_version(version) elems = TMSchema.new("FSP config", version, strict, as_reference=True) elems.add_field( ("fsp_id" if camel_case else "fspID"), int, check_strict=lambda n: n >= 1 and n <= 27, ) elems.add_field( ("function_mode" if camel_case else "functionMode"), str, check_strict=Or("CORR", "PSS-BF", "PST-BF", "VLBI"), ) elems.add_opt_field( "receptors", [ Regex( r"""^(SKA(00[1-9]|0[1-9][0-9]|1[0-2][0-9]|13[0-3]))| (MKT(0[0-5][0-9]|06[0-3]))$""" ) if strict else str ], description=cleandoc( """ Optionally a subset of receptors to be correlated can be specified. If not specified, all receptors that belong to the subarray are cross-correlated (i.e. visibilities for all the baselines in the subarray are generated and transmitted to SDP). Valid receptor IDs include: SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. """ ), ) elems.add_field( ("frequency_slice_id" if camel_case else "frequencySliceID"), int, check_strict=lambda n: n >= 1 and n <= 26, description=cleandoc( """ Frequency Slice to be processed on this FSP (valid range depends on the Frequency Band). """ ), ) if (major, minor) < (3, 0): elems.add_field( ("zoom_factor" if camel_case else "corrBandwidth"), int, check_strict=lambda n: n >= 0 and n <= 6, description=cleandoc( """ Bandwidth to be correlated calculated as FSBW/2n, where n is in range [0..6]. When n=0 the full Frequency Slice bandwidth is correlated. BW > 0 implies ‘Zoom Window’ configuration; the spectral Zoom Window tuning must be specified. """ ), ) elems.add_opt_field( ("zoom_window_tuning" if camel_case else "zoomWindowTuning"), int, description=cleandoc( """ The Zoom Window tuning provided in absolute terms as RF center frequency. Based on that, CSP_Mid calculates tuning within the data stream received from the receptor. Must be selected so that the entire Zoom Window is within the Frequency Slice. If partially out of the FS a warning is generated. If completely outside of the FS an exception is generated. Step size <= 0.01MHz. The Frequency Band Offset can be used to shift the entire observed band in order to accommodate a Zoom Window that spans across a Frequency Slice boundary. """ ), ) if (major, minor) < (3, 0): elems.add_field( ("integration_factor" if camel_case else "integrationTime"), ( And(int, if_strict(validators.validate_integration_factor)) if camel_case else 1400 ), description="Integration time for the " "correlation products, defines multiple of 140 milliseconds.", ) else: elems.add_field( ("integration_factor" if camel_case else "integrationTime"), ( And(int, if_strict(validators.validate_integration_factor)) if camel_case else 1400 ), description=cleandoc( """ Integration time for the correlation products, defines multiple of 140 milliseconds (125 ms starting from AA2). Range: Integer from 1-10 inclusive """ ), ) if (major, minor) < (3, 0): elems.add_opt_field( ("output_mac" if camel_case else "outputMac"), [And([int, str], if_strict(lambda lst: len(lst) == 2))], description=cleandoc( """ Output MAC address to send visibilities to for every channel, given as a list of start channel ID to IEEE 802 MAC addresses. Where no value is given for a concrete channel, the previous value should be used. """ ), ) if (major, minor) < (3, 0): elems.add_opt_field( ("channel_averaging_map" if camel_case else "channelAveragingMap"), [And([int], if_strict(lambda lst: len(lst) == 2))], description=cleandoc( """ Table of up to 20 x 2 integers. Each of entries contains: * Start channel ID, and * averaging factor. Explanation: Each FSP produces 14880 (TBC) fine channels across the correlated bandwidth (Frequency Slice or Zoom Window). Channels are evenly spaced in frequency. TM shall provide the table that for each FSP and each group of 744 channels (there are 20 groups per FSP) indicates the channel averaging factor. More precisely, for each group the TMC provided table specifies: * the channel ID (integer) of the first channel, and * the averaging factor, as follows: * 0 means do not send channels to SDP, * 1 means no averaging, * 2 means average two adjacent channels, * 3 means average three adjacent channels, and so on. If no entry is present for an FSP, the averaging settings of the previous FSP are still applicable. """ ), ) else: elems.add_opt_field( ("channel_averaging_map" if camel_case else "channelAveragingMap"), [And([int], if_strict(lambda lst: len(lst) == 2))], description=cleandoc( """ Table of up to 20 x 2 integers. Each of entries contains: * Start channel ID, and * averaging factor. Explanation: Each FSP produces 14880 (TBC) fine channels across the correlated bandwidth (Frequency Slice or Zoom Window). Channels are evenly spaced in frequency. TM shall provide the table that for each FSP and each group of 744 channels (there are 20 groups per FSP) indicates the channel averaging factor. More precisely, for each group the TMC provided table specifies: The start channel ID will be shifted internally according to the channel_offset value if specified. * the channel ID (integer) of the first channel, and * the averaging factor, as follows: * 0 means do not send channels to SDP, * 1 means no averaging, * 2 means average two adjacent channels, * 3 means average three adjacent channels, and so on. If no entry is present for an FSP, the averaging settings of the previous FSP are still applicable. """ ), ) if (major, minor) < (3, 0): elems.add_opt_field( ("channel_offset" if camel_case else "fspChannelOffset"), int, description=cleandoc( """ Channel ID to use for visibilities of the first channel produced by this FSP. For example, if the channel offset is 5000 the first channel group would span IDs 5000-5743. Note that this offset does not apply to channel maps in this structure (such as `channelAveragingMap` or `outputHost`). """ ), ) else: elems.add_opt_field( ("channel_offset" if camel_case else "fspChannelOffset"), int, description=cleandoc( """ Channel ID to use for visibilities of the first channel produced by this FSP. For example, if the channel offset is 5000 the first channel group would span IDs 5000-5743. Note that this offset does not apply to channel maps in this structure: * `channel_averaging_map` * `output_link_map` * `output_host` * `output_port` """ ), default=0, ) if (major, minor) < (3, 0): elems.add_opt_field( ("output_link_map" if camel_case else "outputLinkMap"), [And([int, str], if_strict(lambda lst: len(lst) == 2))], description=cleandoc( """ Output links to emit visibilities on for every channel, given as a list of start channel ID to link ID. Where no value is given for concrete channel, the previous value should be used. """ ), ) else: elems.add_field( ("output_link_map" if camel_case else "outputLinkMap"), [[int]], check_strict=lambda table: len(table) == 1 and False not in list( map( validators.validate_output_link_map, table, repeat(MAX_CHANNELS_PER_FSP - 1), ) ), description=cleandoc( """ Output links to emit visibilities on for every channel, given as a list of start channel ID to link ID. Where no value is given for concrete channel, the previous value should be used. For AA0.5 and AA1, the link map will only allow a single entry with a link ID of 1. The start channel ID will be shifted internally according to the channel_offset value if specified. Ranges: Start Channel ID: Integer from 0 to 14879 inclusive Link ID: Integer of value 1 """ ), ) if (major, minor) < (3, 0): elems.add_opt_field( ("output_host" if camel_case else "outputHost"), [And([int, str], if_strict(lambda lst: len(lst) == 2))], description=cleandoc( """ Output host to send visibilities to for every channel, given as a list of start channel ID to host IP addresses in dot-decimal notation. Where no value is given for a concrete channel, the previous value should be used. """ ), ) elems.add_opt_field( ("output_port" if camel_case else "outputPort"), [ And( [int], if_strict(lambda lst: len(lst) >= 2 and len(lst) <= 3), ) ], description=cleandoc( """ Output port to send visibilities to for every channel, given as a list of start channel ID to port number. Where no value is given for a concrete channel, the previous value should be used. """ ), ) else: elems.add_opt_field( ("output_host" if camel_case else "outputHost"), [[int, str]], check_strict=lambda table: False not in list( map( validators.validate_output_host, table, repeat(MAX_CHANNELS_PER_FSP - 1), ) ), description=cleandoc( """ Output host to send visibilities to for every channel, given as a list of start channel ID to host IP addresses in dot-decimal notation. Where no value is given for a concrete channel, the previous value should be used. If `output_host` is not specified, then the Mid.CBF will not send any output products to SDP. The start channel ID will be shifted internally according to the channel_offset value if specified. Ranges: Start Channel ID: Integer from channel_offset to (channel_offset + 14879) inclusive Host IP: String w/ any valid IPv4 in dot-decimal notation [0.0.0.0-255.255.255.255] """ ), ) elems.add_opt_field( ("output_port" if camel_case else "outputPort"), [[int]], check_strict=lambda table: validators.validate_output_port( table, major, minor ), description=cleandoc( """ Output port to send visibilities to for every channel, given as a list of start channel ID to port number. When no port number is given for a concrete channel, the previous value will be used. The format is a restricted :ref:`ska-telmodel-channel-map` (see restrictions below) If `output_port` is not specified, then the Mid.CBF will not send any output products to SDP. The start channel ID will be shifted internally according to the channel_offset value if specified. Example:: channel_offset: 111 output_port: [ [0, 9000], [20, 9001], [40, 9002], ... [14840, 9742], [14860, 9743] ] Which will unpack to:: Channel 111 to port 9000 Channel 112 to port 9000 ... Channel 130 to port 9000 Channel 131 to port 9001 ... Channel 150 to port 9001 Channel 151 to port 9002 ... Channel 14950 to port 9741 Channel 14951 to port 9742 ... Channel 14970 to port 9742 Channel 14971 to port 9743 ... Channel 14990 to port 9743 Format Restrictions At most 744 ports can be defined for an FSP The first start channel ID in the list must be 0 Start channel IDs must be in ascending order For AA0.5 and AA1, the start channel IDs must be in increments of 20 At most 20 channels can be sent to the same port per host Start Channel ID: Integer from 0 to 14879 inclusive Port Number: Integer from 0 to 65535 inclusive """ ), ) return elems
[docs] def get_cbf_config_schema(version: str, strict: bool) -> Schema: """Correlator and Beamformer configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the Mid.CBF configuration. """ # Sub-schemas searchWindow = get_search_window_config_schema(version, strict) # cbf specific items camel_case = use_camel_case(version) (major, minor) = split_interface_version(version) if (major, minor) < (4, 0): elems = TMSchema.new( "CBF config", version, strict, description=cleandoc( """ Correlator and Beamformer specific parameters. This section contains the parameters relevant only for CBF sub-element. This section is forwarded only to CBF subelement. Most of it to be borrowed from IICD """ ), as_reference=True, ) else: elems = TMSchema.new( "Mid.CBF config", version, strict, description=cleandoc( """ Correlator and Beamformer specific parameters. This section contains the parameters relevant only for Mid.CBF sub-element. This section is forwarded only to Mid.CBF subelement. """ ), as_reference=True, ) if (major, minor) < (3, 0): elems.add_opt_field( ( "frequency_band_offset_stream1" if camel_case else "frequencyBandOffsetStream1" ), int, description=cleandoc( """ Optionally, an offset can be specified so that the entire observed band is shifted (to accommodate a Zoom Window that crosses a ‘natural’ Frequency Slice boundary). If specified, applies for all the receptors in the sub-array. Bands 1 and 2: input from the receptor consists of a single data stream; the Frequency Band Offset (FBO) should be specified for Stream 1 only. Bands 5a and 5b: input from the receptor consists of two data streams; the FBO can be specified for each stream independently. Note: For Band 5a and 5b the frequency shift is performed by the receptor (DISH). Note: This is optional and does not need to be implemented in PI3, but would be great for demo; if Team Buttons is looking for opportunities to showcase interesting GUIs, Zoom Windows are perfect opportunity (would require TMC and CSP to support these two parameters, corrBandwidth values > 0 and zoom window tuning.) """ ), ) elems.add_opt_field( ( "frequency_band_offset_stream2" if camel_case else "frequencyBandOffsetStream2" ), int, description="See `frequencyBandOffsetStream1`", ) else: elems.add_opt_field( ( "frequency_band_offset_stream1" if camel_case else "frequencyBandOffsetStream1" ), int, check_strict=lambda n: n >= -100000000 and n <= 100000000, description=cleandoc( """ Optionally, an offset can be specified so that the entire observed band is shifted in Hz (to accommodate a Zoom Window that crosses a ‘natural’ Frequency Slice boundary). If specified, applies for all the receptors in the sub-array. Bands 1 and 2: input from the receptor consists of a single data stream; the Frequency Band Offset (FBO) should be specified for Stream 1 only. Bands 5a and 5b: input from the receptor consists of two data streams; the FBO can be specified for each stream independently. Note: For Band 5a and 5b the frequency shift is performed by the receptor (DISH). Range: Integer from -100000000 to 100000000 inclusive (+/- 0.5 * Frequency Slice BW) """ ), ) elems.add_opt_field( ( "frequency_band_offset_stream2" if camel_case else "frequencyBandOffsetStream2" ), int, check_strict=lambda n: n >= -100000000 and n <= 100000000, description=cleandoc( """ See `frequencyBandOffsetStream1` Range: Integer from -100000000 to 100000000 inclusive (+/- 0.5 * Frequency Slice BW) """ ), ) if (major, minor) < (3, 0): elems.add_opt_field( ( "delay_model_subscription_point" if camel_case else "delayModelSubscriptionPoint" ), str, description=cleandoc( """ FQDN of TMC.DelayModel TANGO attribute which exposes delay values for all the dishes assigned to a Subarray in JSON format. Delay values are updated every 10 seconds. """ ), ) else: elems.add_field( ( "delay_model_subscription_point" if camel_case else "delayModelSubscriptionPoint" ), str, description=cleandoc( """ FQDN of TMC.DelayModel TANGO attribute which exposes delay values for all the dishes assigned to a Subarray in JSON format. Delay values are updated every 10 seconds. """ ), ) if (major, minor) < (3, 0): elems.add_opt_field( ( "doppler_phase_corr_subscription_point" if camel_case else "dopplerPhaseCorrSubscriptionPoint" ), str, description=cleandoc( """ The same model applies for all receptors that belong to the subarray. Delivered by TMC using publish-subscribe mechanism (see ICD Section 3.8.8.5.3). The Doppler phase correction, by default, applies only to the CSP_Mid Processing Mode Correlation; optionally may apply to other Processing Modes as well. """ ), ) elems.add_opt_field( ("rfi_flagging_mask" if camel_case else "rfiFlaggingMask"), {}, description=cleandoc( """ Specified as needed in advance of the scan start and/or during the scan. Delivered using publish-subscribe mechanism (see ICD Section 3.8.8.5.7). """ ), ) if (major, minor) < (4, 0): elems.add_field("fsp", [get_fsp_config_schema(version, strict)]) elif (major, minor) < (5, 0): elems.add_field( "correlation", get_correlation_config_schema(version, strict), ) else: elems.add_opt_field( "correlation", get_correlation_config_schema(version, strict), ) # field did not exist prior to this version if (major, minor) >= (5, 0): elems.add_opt_field( "pst_bf", get_pst_bf_config_schema(version, strict), ) elems.add_opt_field("vlbi", get_vlbi_config_schema(version, strict)) elems.add_opt_field("search_window", [searchWindow]) return elems
[docs] def get_search_window_config_schema(version: str, strict: bool) -> Schema: """SearchWindow configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the MID.CBF SearchWindow configuration. """ # Search window schema camel_case = use_camel_case(version) (major, minor) = split_interface_version(version) elems = TMSchema.new( "Search window config", version, strict, description=cleandoc( """ Up to two 300 MHz Search Windows can be optionally configured and used as input for Transient Data Capture and/or Pulsar Search beam-forming. """ ), as_reference=True, ) elems.add_field( ("search_window_id" if camel_case else "searchWindowID"), int, description="Identifier of the 300MHz Search Window. " "Unique within a sub-array.", ) elems.add_field( ("search_window_tuning" if camel_case else "searchWindowTuning"), int, description=cleandoc( """ The Search Window tuning is provided in absolute terms as RF center frequency. The Search Window must be placed within the observed band. If partially out of the observed Band a warning is generated. If completely outside of the observed Band an exception is generated. """ ), ) if (major, minor) < (3, 0): elems.add_field( ("tdc_enable" if camel_case else "tdcEnable"), bool, description="Enable / disable Transient Data Capture" "for the Search Window.", ) elems.add_opt_field( ("tdc_num_bits" if camel_case else "tdcNumBits"), int, description=cleandoc( """ Number of bits per sample (for the Transient Data Capture). Required if TDC is enabled, otherwise not specified. """ ), ) elems.add_opt_field( ( "tdc_period_before_epoch" if camel_case else "tdcPeriodBeforeEpoch" ), int, description=cleandoc( """ Users can trade the period of time for which data are saved and transmitted for the sample bit-width and/or the number of Search Windows. The exact information regarding the memory capacity per receptor and supported range will be provided in construction. The epoch is specified in the command that triggers TDC off-loading (transmission of data). """ ), ) elems.add_opt_field( ( "tdc_period_after_epoch" if camel_case else "tdcPeriodAfterEpoch" ), int, description="see `tdcPeriodBeforeEpoch`", ) elems.add_opt_field( ( "tdc_destination_address" if camel_case else "tdcDestinationAddress" ), [int, str, str, str], description=cleandoc( """ Destination addresses (MAC, IP, port) for off-loading of the content of the Transient Data Capture Buffer, specified per receptor. The destination addresses for the content of the Transient Data Capture can be provided either as a part of the scan configuration or by the command that triggers transmission of the captured data. The latter, if provided, overrides previously set addresses. Required if TDC is enabled, otherwise not specified. """ ), ) return elems
[docs] def get_correlation_config_schema(version: str, strict: bool) -> Schema: """CSP Correlation configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the CSP subarray specific configuration. """ elems = TMSchema.new( "Correlation config", version, strict, description="Correlation specific parameters", as_reference=True, ) elems.add_field( "processing_regions", [get_corr_processing_region_config_schema(version, strict)], ) return elems
[docs] def get_corr_processing_region_config_schema( version: str, strict: bool ) -> Schema: """Correlation Processing Region configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the Correlation Processing Region specific configuration. """ (major, minor) = split_interface_version(version) elems = TMSchema.new( "Correlation Processing Region config", version, strict, description="Parameters that define a processing region of spectrum", as_reference=True, ) elems.add_field( "fsp_ids", [int], check_strict=lambda table: len(table) > 0 and len(table) <= 26 and False not in list(map(lambda n: n >= 1 and n <= 27, table)), description=cleandoc( """ List of FSPs to be used for the processing region For non-zoom, the limit is 13 because that is the number of FSPs needed to process data from one 2.5 GHz sub-band, which is the maximum bandwidth that can be stitched together. Band 5 sub-bands (a and b) cannot be combined in a single processing region. Ranges: Array Length: Array of 1 to 26 Integers inclusive Array Element: Integer from 1 to 27 inclusive Notes: AA0.5 supports an array of 1 to 4 integers AA1 supports an array of 1 to 8 integers """ ), ) elems.add_opt_field( "receptors", [Or(MID_SKA, MID_MKT) if strict else str], description=cleandoc( """ Optionally a subset of receptors to be correlated can be specified. If not specified, all receptors that belong to the subarray are cross-correlated (i.e. visibilities for all the baselines in the subarray are generated and transmitted to SDP). Valid receptor IDs include: SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. """ ), ) if (major, minor) >= (4, 1): elems.add_field( "start_freq", int, check_strict=lambda n: n >= 0 and n <= 15400000000, description=cleandoc( """ Center frequency of the first fine channel in the output product in Hz. Range: Integer from 0 to 15400000000 inclusive Notes: AA0.5 Supports start_freq of 0Hz to 1980MHz AA0.5 Mid.CBF will produce a warning when the bandwidth [(start_freq - channel_width/2) to (start_freq + (channel_count - 1) * channel_width + channel_width/2)] of the region is outside the range of bands 1 & 2 (350MHz - 1760MHz) """ ), ) else: elems.add_field( "start_freq", int, check_strict=lambda n: n >= 350000000 and n <= 15400000000, description=cleandoc( """ Center frequency of the first fine channel in the output product in Hz. Note: Range for start_freq has been specified to cover the frequency range of all bands, however the entire processing region ((start_freq - channel_width/2) + channel_width * channel_count) must fall within the frequency range for the band specified for the scan configuration Range: Integer from 350000000 to 15400000000 inclusive """ ), ) elems.add_field( "channel_width", int, check_strict=lambda n: n in CHANNEL_WIDTH_VALUES, description=cleandoc( """ The width of each fine channel in Hz. Allowed channel widths are a fraction/multiple of the default fine channel bandwidth: 13440 Hz. Zoom mode will use channel width less than 13440 Hz. Channel averaging will use channel width more than 13440 Hz. Range: Enum value that is one of:: 210 420 840 1680 3360 6720 13440 26880 40320 53760 80640 107520 161280 215040 322560 416640 430080 645120 Note: AA0.5/1 only supports 13440 channel width value """ ), ) elems.add_field( "channel_count", int, check_strict=lambda value: value >= 1 and value <= MAX_CORR_CHANNELS, description=cleandoc( """ Number of fine channels in the output product. Range: Integer from 1 to 2147483647 inclusive Note: AA0.5/1: Integer from 1 to 58982 inclusive and must be a multiple of 20 """ ), ) elems.add_field( "sdp_start_channel_id", int, check_strict=lambda n: n >= 0 and n <= MAX_CORR_CHANNELS, description=cleandoc( """ In the output products (visibility packets) sent to SDP, Mid.CBF will number channels sequentially in increments of 1 starting at the sdp_start_channel_id. sdp_start_channel_id allows TMC to configure Mid.CBF to provide SDP with unique channel IDs for every channel in a sub-array when multiple processing regions are used. Mid.CBF does not require or validate that each channel in a sub-array will have a unique channel ID. The channel ID provided to SDP will be the zero based channel number within the processing region plus the sdp_start_channel_id specified for the processing region. This means the channel with the center frequency of start_freq will have a channel ID of sdp_start_channel_id. Range: Integer from 0 to 2147483647 inclusive """ ), ) if (major, minor) < (5, 0): elems.add_field( "integration_factor", int, check_strict=lambda n: validators.validate_integration_factor(n), description=cleandoc( """ Integration time for the correlation products, defines multiple of 140 milliseconds. Range: Integer from 1-10 inclusive """ ), ) else: elems.add_field( "integration_factor", int, check_strict=lambda n: validators.validate_integration_factor(n), description=cleandoc( """ Integration time for the correlation products, defines multiple of 140 milliseconds. .. list-table:: :widths: auto :header-rows: 1 * - integration_factor value - Interval (s) * - 1 - 0.14 * - 2 - 0.28 * - 3 - 0.42 * - 4 - 0.56 * - 5 - 0.70 * - 6 - 0.84 * - 7 - 0.98 * - 8 - 1.12 * - 9 - 1.26 * - 10 - 1.40 Range: Integer from 1-10 inclusive """ ), ) elems.add_opt_field( "output_host", [[int, str]], check_strict=lambda table: len(table) > 0 and False not in list( map( validators.validate_output_host, table, repeat(MAX_CORR_CHANNELS), ) ), description=cleandoc( """ Output host to send visibilities to for every channel, given as a list of start channel ID to host IP addresses in dot-decimal notation. Where no value is given for a concrete channel, the previous value should be used. The first channel id in the list needs to be the same as sdp_start_channel_id and then all the rest of the numbers are relative to the sdp_start_channel_id. If `output_host` is not specified, then the Mid.CBF will not send any output products to SDP. Ranges: Start Channel ID: Integer from 0 to 2147483647 inclusive Host IP: String w/ any valid IPv4 in dot-decimal notation [0.0.0.0-255.255.255.255] """ ), ) elems.add_opt_field( "output_port", [[int]], check_strict=lambda table: validators.validate_output_port( table, major, minor ), description=cleandoc( """ Output port to send visibilities to for every channel, given as a list of start channel ID to port number. When no port number is given for a concrete channel, the previous value will be used. The format is a restricted :ref:`ska-telmodel-channel-map` (see restrictions below) The first channel id in the list needs to be the same as sdp_start_channel_id and then all the rest of the numbers are relative to the sdp_start_channel_id. If `output_port` is not specified, then the Mid.CBF will not send any output products to SDP. Example:: sdp_start_channel_id: 120 output_port: [ [120, 9000], [140, 9001], [160, 9002], ... [14840, 9742], [14860, 9743] ] Which will unpack to:: Channel 120 to port 9000 Channel 121 to port 9000 ... Channel 139 to port 9000 Channel 140 to port 9001 ... Channel 159 to port 9001 Channel 160 to port 9002 ... Channel 14839 to port 9741 Channel 14940 to port 9742 ... Channel 14859 to port 9742 Channel 14860 to port 9743 ... Channel 14879 to port 9743 Format Restrictions The first start channel id must match sdp_start_channel_id Start channel IDs must be in ascending order For AA0.5 and AA1, the start channel IDs must be in increments of 20 At most 20 channels can be sent to the same port per host Start Channel ID: Integer from 0 to 2147483647 inclusive Port Number: Integer from 0 to 65535 inclusive """ ), ) elems.add_field( "output_link_map", [[int]], check_strict=lambda table: len(table) == 1 and False not in list( map( validators.validate_output_link_map, table, repeat(MAX_CORR_CHANNELS), ) ), description=cleandoc( """ Output links to emit visibilities on for every channel, given as a list of start channel ID to link ID. Where no value is given for concrete channel, the previous value should be used. The first channel id in the list needs to be the same as sdp_start_channel_id and then all the rest of the numbers are relative to the sdp_start_channel_id. For AA0.5 and AA1, the link map will only allow a single entry with a link ID of 1. Ranges: Start Channel ID: Integer from 0 to 2147483647 inclusive Link ID: Integer of value 1 """ ), ) return elems
[docs] def get_pst_bf_config_schema(version: str, strict: bool) -> Schema: """CSP Pulsar Search Timing Beam-Former configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the CSP subarray specific configuration. """ elems = TMSchema.new( "PST-BF config", version, strict, description="PST Beam-Former specific parameters", as_reference=True, ) processing_region_schema = get_pst_bf_processing_region_config_schema( version, strict ) elems.add_field( "processing_regions", [processing_region_schema], check_strict=And( Schema( name="At least one processing region", schema=lambda value: len(value) >= 1, ), Schema( name="Processing regions have unique FSP IDs", schema=lambda value: ( validators.processing_regions_have_unique_fsp_ids(value) ), ), Schema( name="first output_host mapping start channel matches " + "pst_start_channel_id", schema=lambda value: ( validators.timing_beams_start_with_start_value( map_property="output_host", start_property="pst_start_channel_id", processing_regions=value, ) ), ), Schema( name="first output_port mapping start channel matches " + "pst_start_channel_id", schema=lambda value: ( validators.timing_beams_start_with_start_value( map_property="output_port", start_property="pst_start_channel_id", processing_regions=value, ) ), ), Schema( name="first output_link_map mapping start channel matches " + "pst_start_channel_id", schema=lambda value: ( validators.timing_beams_start_with_start_value( map_property="output_link_map", start_property="pst_start_channel_id", processing_regions=value, ) ), ), ), ) return elems
[docs] def get_pst_bf_processing_region_config_schema( version: str, strict: bool, ) -> Schema: """PST Beam-Former Processing Region configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the PST Beam-Former Processing Region specific configuration. """ elems = TMSchema.new( "PST Processing Region config", version, strict, description="Parameters that define a processing region of spectrum", as_reference=True, ) elems.add_field( "fsp_ids", check=[int], check_strict=And( Schema( name="Array length within the valid of range of 1 to 26 " + "inclusive", schema=lambda value: len(value) >= 1 and len(value) <= 26, ), Schema( name="Array values within the valid range of 1 to 27 " + "inclusive", schema=lambda value: False not in list(map(lambda n: n >= 1 and n <= 27, value)), ), Schema( name="Array values are unique", schema=lambda value: len(value) == len(set(value)), ), ), description=cleandoc( """ List of FSPs to be used for the processing region Ranges: * Array Length: Array of 1 to 26 Integers inclusive * Array Element: Integer from 1 to 27 inclusive Notes: For AA0.5/1: * Array Length of 1 integer * Array values of range 1-8 inclusive """ ), ) max_pst_bf_freq_range_hz = 15399982080 pst_bf_channel_width_hz = 53760 elems.add_field( "start_freq", int, check_strict=And( Schema( name="Is within the valid range of 0 to " + f"{max_pst_bf_freq_range_hz} inclusive", schema=lambda value: value >= 0 and value <= max_pst_bf_freq_range_hz, ), Schema( name=f"is a multiple of {pst_bf_channel_width_hz} Hz", schema=lambda value: value % pst_bf_channel_width_hz == 0, ), ), description=cleandoc( f""" Center frequency of the first fine channel in the output product in Hz. Range: Integer from 0 Hz to {max_pst_bf_freq_range_hz} Hz inclusive Checks: * Must be a multiple of {pst_bf_channel_width_hz} Hz Notes: For AA0.5/1: ``start_freq`` is a Enum value that is one of: Band 1:: 296862720 495075840 693235200 891448320 Band 2:: 891448320 1089607680 1287767040 1485980160 1684139520 """ ), ) max_channel_count = 47923 elems.add_field( "channel_count", int, check_strict=Schema( name=f"Is within the valid range of 1 to {max_channel_count} " + "inclusive", schema=lambda value: value >= 1 and value <= max_channel_count, ), description=cleandoc( f""" Number of fine channels in the output product. Max channel_count calculated as: ``198,180,164 Hz per FS * 13 FS / 53760 Hz per channel = 47,923`` Range: * Integer from 1 to {max_channel_count} inclusive Notes: For AA0.5/1: * The only valid value is 3700 """ ), ) elems.add_field( "pst_start_channel_id", int, check_strict=Schema( name=f"Is within the valid range of 0 to {MAX_SIGNED_32BIT_INT} " + "inclusive", schema=lambda value: value >= 0 and value <= MAX_SIGNED_32BIT_INT, ), description=cleandoc( f""" Starting value for the channel numbering in the processing region. Used only to number channels in the output product (timing beam). Channels in the output product are numbered sequentially starting with the ``pst_start_channel_id``. The channel with the centre frequency of start_freq will have a channel ID of ``pst_start_channel_id``. The next channel will have the channel ID of ``pst_start_channel_id`` + 1. Range: * Integer from 0 to {MAX_SIGNED_32BIT_INT} inclusive """ ), ) elems.add_field( "timing_beams", [get_pst_timing_beam_config_schema(version, strict)], check_strict=Schema( name="Is array length within the valid range of 1 to 16 inclusive", schema=lambda value: len(value) > 0 and len(value) <= 16, description=cleandoc( """ Parameters that define a timing beam Range: * Array length between 1 to 16 inclusive. Notes: For AA0.5/1: * Array length of 1 """ ), ), ) return elems
[docs] def get_pst_timing_beam_config_schema(version: str, strict: bool) -> Schema: """Timing beam configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the Timing beam specific configuration. """ elems = TMSchema.new( "Timing beam config", version, strict, description="Parameters that define a timing beam", as_reference=True, ) elems.add_field( "timing_beam_id", int, check_strict=Schema( name="Is within the valid range of 1 to 16 inclusive", schema=lambda value: value >= 1 and value <= 16, ), description=cleandoc( """ Identifier for a beam timing Each ``timing_beam_id`` can only be used by one subarray at a time. Range: * 1 to 16 inclusive """ ), ) elems.add_opt_field( "receptors", [Or(MID_SKA, MID_MKT) if strict else str], description=cleandoc( """ Optionally a subset of receptors to be included in the timing beam can be specified. If not specified, all receptors that belong to the subarray are included in the timing beam. Valid receptor IDs include: * SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. * MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. Note: For AA0.5/1: * If receptors are specified for one FSP, the same receptors need to be specified for all the FSPs. """ ), ) elems.add_field( "output_link_map", [[int]], check_strict=And( Schema( name="At least one mapping is provided", schema=lambda value: len(value) != 0, ), Schema( name="Each mapping is of length 2", schema=lambda value: False not in list(map(lambda n: len(n) == 2, value)), ), Schema( name="Start channel ID is within the valid range of 0 to " + f"{MAX_SIGNED_32BIT_INT} inclusive", schema=lambda value: False not in list( map( lambda n: n[0] >= 0 and n[0] <= MAX_SIGNED_32BIT_INT, value, ) ), ), Schema( name="Start channel IDs are in ascending order", schema=lambda value: validators.channel_ids_in_asc_order( value ), ), Schema( name="Link value greater than 0", schema=lambda value: False not in list(map(lambda n: n[1] > 0, value)), ), ), description=cleandoc( """ Output links to emit timing beam on for every channel, given as a list of start channel ID to link ID. Where no value is given for concrete channel, the previous value should be used. Ranges: Start Channel ID: Integer from 0 to 14879 inclusive Link ID: Positive Integer of value Checks: * At least one ``output_link_map`` mapping required * Start Channel IDs are in ascending order * In the first mapping entry, the Start Channel ID must match ``pst_start_channel_id`` Note: For AA0.5/1: * ``output_link_map`` only allows a single entry with a link ID of 1. """ ), ) packet_size = 185 elems.add_field( "output_host", [[int, str]], check_strict=And( Schema( name="At least one mapping is provided", schema=lambda value: len(value) != 0, ), Schema( name="Start channel IDs are within the valid range of 0 " + f"to {MAX_SIGNED_32BIT_INT} inclusive", schema=lambda value: False not in list( map( lambda n: n[0] >= 0 and n[0] <= MAX_SIGNED_32BIT_INT, value, ) ), ), Schema( name="Start channel IDs are in ascending order", schema=lambda value: validators.channel_ids_in_asc_order( value ), ), Schema( name="Host must be a valid IPv4 address", schema=lambda value: False not in list( map( lambda n: IPV4_REGEX_PATTERN.fullmatch(n[1]) is not None, value, ) ), ), ), description=cleandoc( f""" Output host to send timing beam to for every channel, given as a list of start channel ID to host IP addresses in dot-decimal notation. Where no value is given for a concrete channel, the previous value should be used. The first start channel ID will be the `pst_start_channel_id` Ranges: * Start Channel ID: Integer from 0 to {MAX_SIGNED_32BIT_INT} inclusive * Host IP: String w/ any valid IPv4 in dot-decimal notation [0.0.0.0-255.255.255.255] Checks: * At least one `output_host` mapping required * Start channel IDs are in ascending order * In the first mapping entry, the Start Channel ID must match ``pst_start_channel_id`` Note: For AA0.5/1 : * Only one `output_host` mapping allowed * Difference in Start chanenel IDs are a multiple of {packet_size} * Start Channel ID Range is between ``pst_start_channel_id`` and ``(pst_start_channel_id + (channel_count - 185))`` inclusive """ ), ) elems.add_field( "output_port", check=[[int]], check_strict=And( Schema( name="At least one mapping is provided", schema=lambda value: len(value) != 0, ), Schema( name="Each mapping is of length 2", schema=lambda value: False not in list(map(lambda n: len(n) == 2, value)), ), Schema( name="Start channel IDs are within the valid range of 0 " + f"to {MAX_SIGNED_32BIT_INT} inclusive", schema=lambda value: False not in list( map( lambda n: n[0] >= 0 and n[0] <= MAX_SIGNED_32BIT_INT, value, ) ), ), Schema( name="Start channel IDs are in ascending order", schema=lambda value: validators.channel_ids_in_asc_order( value ), ), Schema( name="Port numbers are with the valid range of 0 to " + f"{MAX_PORT_VALUE} inclusive", schema=lambda value: False not in list( map(lambda n: n[1] >= 0 and n[1] <= MAX_PORT_VALUE, value) ), ), ), description=cleandoc( """ Output port to send beam timing to for every channel, given as a list of start channel ID to port number. When no port number is given for a concrete channel, the previous value will be used. The format is a restricted :ref:`ska-telmodel-channel-map` (see restrictions below) Ranges: * Start Channel ID: Integer from 0 to 2147483647 inclusive * Port Number: Integer from 0 to 65535 inclusive Format Restrictions: * At least one ``output_port`` mapping is required * The first start channel ID in the list must be ``pst_start_channel_id`` * Start channel IDs must be in ascending order Note: For AA0.5/1: * Only one ``output_port`` entry allowed * Start channel IDs must be in increments of 185 * At most 3700 channels can be sent to the same port per host """ ), ) return elems
[docs] def get_subarray_config_schema(version: str, strict: bool) -> Schema: """CSP Subarray configuration schema :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the CSP subarray specific configuraiton. """ # # subarray specific items camel_case = use_camel_case(version) elems = TMSchema.new( "subarray", version, strict, description=cleandoc( """ subarray section, containing the parameters relevant only for the current sub-array device. This section is not forwarded to any subelement. """ ), ) elems.add_field( ("subarray_name" if camel_case else "subarrayName"), str, description=cleandoc( """ Name and scope of current subarray the sub-array. """ ), ) return elems
[docs] def get_common_config_schema(version: str, strict: bool) -> Schema: """CSP Subarray common configuration schema. This section is valid for Mid CSP because it includes some parameters that are Mid.CBF specific. The set of parameters that are common to Mid and Low CSP, are retrieved by the _get_common_config_schema method defined in the common_schema python module. :param version: Interface Version URI :param strict: Schema strictness :return: the JSON Schema for the CSP subarray common configuration (ADR-18). """ camel_case = use_camel_case(version) (major, minor) = split_interface_version(version) elems = _get_common_config_schema_without_band(version, strict) elems.add_opt_field( ("band_5_tuning" if camel_case else "band5Tuning"), [float], description=cleandoc( """ Center frequency for the Band-of-Interest. Required if Band is 5a or 5b; not specified for other Bands (not configurable for Band 1 and 2). Input for Band 5a and 5b consists of two 2.5 GHz streams; the center frequency can be independently tuned for each stream. The following nomenclature is used to refer to Band 5a and 5b streams: 5a1, 5a2, 5b1, 5b2. """ ), ) add_mid_frequency_band(version, strict, elems) # In versions before 1.0, FSPs were part of the common schema if (major, minor) < (1, 0): elems.add_field("fsp", [get_fsp_config_schema(version, strict)]) # # In version 1.0, subarrayID was added if (major, minor) == (1, 0): elems.add_field( "subarray_id" if camel_case else "subarrayID", int, description="Subarray number", ) return elems
[docs] def get_csp_config_schema( version: str, strict: bool, tmc_schema_uri: str = "" ) -> Schema: """Generates the CSP configuration schema based on the given version. :param version: Interface version :param strict: Strict mode - refuse even harmless schema violations (like extra keys). :param tmc_schema_uri: TMC Interface version (optional). :return: The JSON Schema for the CSP configuration. :raise: `ValueError` exception on mismatch major version or invalid JSON Schema URI. """ # Normalize and extract version details version = csp_version.normalize_csp_config_version(version) major, minor = split_interface_version(version) # Validate version csp_version.check_csp_interface_version( version, [csp_version.CSP_CONFIG_PREFIX, csp_version.CSP_CONFIGSCAN_PREFIX], ) # Get subsystem versions _, pst_version = csp_version.get_csp_config_subsystem_version( version, "pst" ) _, pss_version = csp_version.get_csp_config_subsystem_version( version, "pss" ) _, cbf_version = csp_version.get_csp_config_subsystem_version( version, "midcbf" ) # Create schema object schema = TMSchema.new("CSP config", version, strict) # Handle version 0 (pre-ADR-18 format) if major == 0: schema.update(get_common_config_schema(version, strict)) return schema # Add common fields for major versions >= 1 schema.add_field("interface", str) if (major, minor) >= (2, 6): schema.add_opt_field( "transaction_id", str, description="A transaction ID specific to the command", ) schema.add_opt_field( "subarray", get_subarray_config_schema(version, strict) ) schema.add_opt_field( "pss", get_csp_pss_configure_schema(pss_version, strict) ) # add COMMON schema.add_field("common", get_common_config_schema(version, strict)) # add MID CBF cbf_attr_name = "" if major < 4: cbf_attr_name = "cbf" else: # CBF attribute change name at version 4 cbf_attr_name = "midcbf" schema.add_field( cbf_attr_name, get_csp_cbf_config_schema(cbf_version, strict) ) _update_config_schema_for_OSO_TMC( schema, version, tmc_schema_uri, cbf_attr_name ) schema.add_opt_field( "pst", get_csp_pst_config_schema(version, pst_version, strict) ) return schema
[docs] def get_csp_cbf_config_schema(version: str, strict: bool) -> Schema: """Wrapper to retrieve the configuration schema for the Correlator and Beamformer (CBF) as part of the CSP (Central Signal Processor) interface. :param version: Interface Version URI :param strict: If True, enforce strict validation rules in the schema. :return: the JSON Schema for the Mid.CBF configuration. """ return get_cbf_config_schema(version, strict)
def _update_config_schema_for_OSO_TMC( schema: TMSchema, csp_uri: str, tmc_schema_uri: str = "", cbf_schema_name: str = "", ): """ Update the configure schema for OSO to TMC. The attribute are removed directly from the schema :param schema: configure TMSchema that has to be updated :param csp_uri: Interface version :param tmc_schema_uri: tmc URI version (optional). Used to differentiate the OSO-TMC-CSP schema according to the TMC interface version. """ major, _ = split_interface_version(csp_uri) if tmc_schema_uri and major >= 4: # remove attributes from CBF if "delay_model_subscription_point" in schema[cbf_schema_name]: schema[cbf_schema_name].__delitem__( "delay_model_subscription_point" ) if "correlation" in schema[cbf_schema_name]: for pr_regions in schema[cbf_schema_name]["correlation"][ "processing_regions" ]: if "output_host" in pr_regions: pr_regions.__delitem__("output_host") if "output_port" in pr_regions: pr_regions.__delitem__("output_port") if "output_link_map" in pr_regions: pr_regions.__delitem__("output_link_map") tmc_version = split_interface_version(tmc_schema_uri) if tmc_version > (3, 0): if "subarray_id" in schema["common"]: schema["common"].__delitem__("subarray_id") # elif "subarrayID" in schema["common"]: # schema["common"].__delitem__("subarrayID")
[docs] def get_csp_scan_schema(version: str, strict: bool) -> Schema: """ Returns the schema to verify the CSP scan command. :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ scan_schema = TMSchema.new("CSP scan", version, strict) (major, minor) = split_interface_version(version) scan_schema.add_field( "interface", str, description="URI of JSON schema applicable to this JSON payload.", ) if (major, minor) >= (2, 3): scan_schema.add_opt_field( "transaction_id", str, description="A transaction id specific to the command", ) scan_schema.add_field( "scan_id", int, description="Scan ID to associate with the data.", ) return scan_schema
[docs] def get_csp_assignresources_schema(version: str, strict: bool) -> Schema: """ Returns the schema to verify the CSP assignresources command. :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ camel_case = use_camel_case(version) (major, minor) = split_interface_version(version) assignresources_schema = TMSchema.new( "CSP assignresources", version, strict ) assignresources_schema.add_field( "interface", str, description="URI of JSON schema applicable to this JSON payload.", ) if (major, minor) >= (2, 3): assignresources_schema.add_opt_field( "transaction_id", str, description="A transaction id specific to the command", ) if (major, minor) < (3, 0): assignresources_schema.add_field( "subarray_id" if camel_case else "subarrayID", int, description=cleandoc( """ The Subarray ID that the list of receptors will be assigned to. For Mid, there are a maximum of 16 subarrays. """ ), ) else: assignresources_schema.add_field( "subarray_id" if camel_case else "subarrayID", int, check_strict=lambda n: n >= 1 and n <= 16, description=cleandoc( """ The Subarray ID that the list of receptors will be assigned to. For Mid, there are a maximum of 16 subarrays. Range: Integer from 1-16 inclusive """ ), ) # Add pst schema to the assign resources schema get_csp_pst_assign_release_resources( version, assignresources_schema, strict ) # Add pst schema to the assign resources schema get_csp_pss_assign_release_resources( version, assignresources_schema, strict ) dish_elements = TMSchema.new("dish assignresources", version, strict) dish_elements.add_field( "receptor_ids", [ Regex( r"""^(SKA(00[1-9]|0[1-9][0-9]|1[0-2][0-9]|13[0-3]))| (MKT(0[0-5][0-9]|06[0-3]))$""" ) if strict else str ], description=cleandoc( """ The list of receptors that will be assigned to the Subarray ID. Receptor IDs can be any string, not necessarily numbers. Valid receptor IDs include: SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. """ ), ) assignresources_schema.add_field( "dish", dish_elements, ) return assignresources_schema
[docs] def get_csp_endscan_schema(version: str, strict: bool) -> Schema: """ Returns the schema to verify the CSP endscan command. :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ endscan_schema = TMSchema.new("CSP endscan", version, strict) (major, minor) = split_interface_version(version) endscan_schema.add_field( "interface", str, description="URI of JSON schema applicable to this JSON payload.", ) if (major, minor) >= (2, 3): endscan_schema.add_opt_field( "transaction_id", str, description="A transaction id specific to the command", ) endscan_schema.add_field( "scan_id", int, description="Scan ID to end.", ) return endscan_schema
[docs] def get_csp_releaseresources_schema(version: str, strict: bool) -> Schema: """ Returns the schema to verify the CSP releaseresources command. :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ camel_case = use_camel_case(version) (major, minor) = split_interface_version(version) releaseresources_schema = TMSchema.new( "CSP releaseresources", version, strict ) (major, minor) = split_interface_version(version) releaseresources_schema.add_field( "interface", str, description="URI of JSON schema applicable to this JSON payload.", ) if (major, minor) >= (2, 3): releaseresources_schema.add_opt_field( "transaction_id", str, description="A transaction id specific to the command", ) if (major, minor) < (3, 0): releaseresources_schema.add_field( "subarray_id" if camel_case else "subarrayID", int, description="Subarray ID which will have its resource(s) " "released.", ) else: releaseresources_schema.add_field( "subarray_id" if camel_case else "subarrayID", int, check_strict=lambda n: n >= 1 and n <= 16, description=cleandoc( """ Subarray ID which will have its resource(s) released. Range: Integer from 1-16 inclusive """ ), ) # Add pst schema to the release resources schema get_csp_pst_assign_release_resources( version, releaseresources_schema, strict ) # Add pss schema to the release resources schema get_csp_pss_assign_release_resources( version, releaseresources_schema, strict ) releaseresources_schema.add_opt_field( "release_all", bool, description=cleandoc( """ Set to true if you wish to release all resources assigned to the Subarray. """ ), ) releaseresources_schema.add_opt_field( "receptor_ids", [ Regex( r"""^(SKA(00[1-9]|0[1-9][0-9]|1[0-2][0-9]|13[0-3]))| (MKT(0[0-5][0-9]|06[0-3]))$""" ) if strict else str ], description=cleandoc( """ The list of receptors that will be released from the Subarray ID. Receptor IDs can be any string, not necessarily numbers. Valid receptor IDs include: SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. """ ), ) return releaseresources_schema
[docs] def get_csp_delaymodel_schema(version: str, strict: bool) -> Schema: """ Returns the schema to verify the CSP delaymodel command. :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ (major, minor) = split_interface_version(version) delaymodel_schema = TMSchema.new( "CSP delaymodel", version, strict, ) delaymodel_schema.add_field( "interface", str, description="URI of JSON schema applicable to this JSON payload.", ) if (major, minor) >= (3, 0): delaymodel_schema.add_field( "start_validity_sec", float, check_strict=lambda n: n > 0.0, description=cleandoc( """ Time when delay model becomes valid (when Mid.CBF shall apply the new model), specified as the number of seconds since the 1999-12-31T23:59:28Z UTC (SKA epoch). Note: Delay models need to be published at least 5 seconds before the validity period starts Range: Non-zero positive number """ ), ) delaymodel_schema.add_field( "cadence_sec", float, check_strict=lambda n: n > 0.0, description=cleandoc( """ The time in SI seconds of the planned validity period of the delay model, measured from start_validity_sec. Also indicates that the next delay model should be issued no more than cadence_sec later than the current delay model that was issued. This is a configurable field and may change during operations, but the expected value for Mid.CBF is 10 seconds. Mid.CBF will expect the next delay model it receives to have a start_validity_sec <= (current start_validity_sec + cadence_sec). If such a delay model does not arrive, Mid.CBF will continue to use the current delay model, up to the maximum acceptable validity period, which is validity_period_sec. At that point, if a new delay model still hasn't arrived, Mid.CBF will stop processing (including outputting products) and will issue an error message. Range: Non-zero positive number """ ), ) delaymodel_schema.add_field( "validity_period_sec", float, check_strict=lambda n: n > 0.0, description=cleandoc( """ The maximum acceptable delay model validity period in SI seconds, starting at start_validity_sec. This is a configurable field and may change during operations, but the expected value for Mid.CBF is 30 seconds. If Mid.CBF has not received, as expected, a new delay model with a new start_validity_sec <= (start_validity_sec + cadence_sec), it will continue to use the current delay model for up to validity_period_sec seconds. At that point, if a new delay model still hasn't arrived, Mid.CBF will stop processing (including outputting products) and will issue an error message. Range: Non-zero positive number """ ), ) delaymodel_schema.add_field( "config_id", str, description=cleandoc( """ The configuration ID of the scan that this delay model applies to. Corresponds to "config_id" provided in the scan configuration. This field is used to ensure that the CBF does not use delay models from a previous observation at the start of a new observation. """ ), ) delaymodel_schema.add_field( "subarray", int, check_strict=lambda n: n >= 1 and n <= 16, description=cleandoc( """ The subarray to which the delay models apply. Range: Integer from 1-16 inclusive """ ), ) delaymodel_schema.add_field( "receptor_delays", [get_csp_delay_details_schema(version, strict)], ) elif (major, minor) == (2, 2): delaymodel_schema.add_field( "epoch", float, description=cleandoc( """ Time when delay model becomes valid (when Mid.CBF shall apply the new model) specified as an offset in seconds, expressed as a float number, from 1999-12-31T23:59:28Z UTC (which is called the 'SKA epoch'). Range: 64-bit number """ ), ) delaymodel_schema.add_field( "validity_period", float, description=cleandoc( """ validity period of the delay model (starting at epoch) [s] Range: positive number """ ), ) delaymodel_schema.add_field( "delay_details", [get_csp_delay_details_schema(version, strict)], ) return delaymodel_schema
[docs] def get_csp_delay_details_schema(version: str, strict: bool) -> Schema: """ Returns the schema with the CSP delay details :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ (major, minor) = split_interface_version(version) delaydetails_schema = TMSchema.new( "delay details", version, strict, as_reference=True, ) if (major, minor) >= (3, 0): delaydetails_schema.add_field( "receptor", ( Regex( r"""^(SKA(00[1-9]|0[1-9][0-9]|1[0-2][0-9]|13[0-3]))| (MKT(0[0-5][0-9]|06[0-3]))$""" ) if strict else str ), description=cleandoc( """ The Receptor (Dish) ID to which the xypol_coeffs_ns and ypol_offset_ns apply. Valid receptor IDs include: SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. """ ), ) delaydetails_schema.add_field( "xypol_coeffs_ns", [float], description=cleandoc( """ The delay model for a receptor is specified as a set of coefficients for a 5th order polynomial. Coefficients of the polynomial are specified as an array. The Coefficients apply to both X and Y polarizations. The delay at time t, where t is measured with respect the beginning of the validity interval, is calculated as: d(t) = c0 + c1*t + c2*t^2 + c3*t^3 + c4*t^4 + c5*t^5 Units for coefficients c0,c1,...,c5: ns/s^k where: k=0,1,...,5 ns=nanoseconds s=seconds Type: 64 bit floating point number """ ), ) delaydetails_schema.add_field( "ypol_offset_ns", float, description=cleandoc( """ Constant delay offset of polarization Y with respect to polarization X, in nanoseconds. Type: 64 bit floating point number """ ), ) elif (major, minor) == (2, 2): delaydetails_schema.add_field( "receptor", ( Regex( r"""^(SKA(00[1-9]|0[1-9][0-9]|1[0-2][0-9]|13[0-3]))| (MKT(0[0-5][0-9]|06[0-3]))$""" ) if strict else str ), description=cleandoc( """ The Receptor (Dish) ID to which the poly_info coeffs apply. Valid receptor IDs include: SKA dishes: "SKAnnn", where nnn is a zero padded integer in the range of 001 to 133. MeerKAT dishes: "MKTnnn", where nnn is a zero padded integer in the range of 000 to 063. Range: any string """ ), ) delaydetails_schema.add_field( "poly_info", [get_csp_poly_info_schema(version, strict)], ) return delaydetails_schema
[docs] def get_csp_poly_info_schema(version: str, strict: bool) -> Schema: """ Returns the schema with the CSP delay details :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ polyinfo_schema = TMSchema.new( "poly info", version, strict, as_reference=True, ) polyinfo_schema.add_field( "polarization", str, description=cleandoc( """ Polarization of the delay model entry Range: X or Y """ ), ) polyinfo_schema.add_field( "coeffs", [float], description=cleandoc( """ Delay Model is specified as coefficients for a 5th order polynomial. Coefficients of the polynomial are specified as an array. The delay at time t, where t is measured with respect the beginning of the validity interval is calculated as: d(t) = c0 + c1*t + c2*t^2 + c3*t^3 + c4*t^4 + c5*t^5 Units for coefficients c0,c1,..c5: ns/s^k where k=0,1,..5 Range for coefficients: 64 bit number """ ), ) return polyinfo_schema
[docs] def get_csp_low_delaymodel_schema(version: str, strict: bool) -> Schema: """ Returns the schema to verify the CSP low delaymodel command. :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ delaymodel_schema = TMSchema.new( "CSP low delaymodel", version, strict, ) delaymodel_schema.add_field( "interface", str, description=cleandoc( """ URI of JSON schema applicable to this JSON payload. Type: str """ ), ) delaymodel_schema.add_field( "start_validity_sec", float, description=cleandoc( """ Time when delay model becomes valid Start point of polynomial validity no sensible default. It indicates an epoch, which could be anytime. Type: float """ ), ) delaymodel_schema.add_field( "cadence_sec", float, check_strict=lambda n: n > 0.0, description=cleandoc( """ The time in seconds between updates/publications of the delay polynomials. Type: float Range: Non-zero positive number """ ), ) delaymodel_schema.add_field( "validity_period_sec", float, check_strict=lambda n: n > 0.0, description=cleandoc( """ Validity period of the delay model (starting at epoch) [s] Type: float Range: Non-zero positive number """ ), ) delaymodel_schema.add_field( "config_id", str, description=cleandoc( """ A string, should be the same as the equivalent value in the last "configure" JSON. If not it indicates that these are not yet valid polys for the current configuration. Type: str """ ), ) delaymodel_schema.add_field( "station_beam", int, check_strict=lambda n: n >= 1 and n <= 48, # range description=cleandoc( """ The station beams for which the delay polynomials apply to. Type: int Range: Integer from 1-48 inclusive """ ), ) delaymodel_schema.add_field( "subarray", int, check_strict=lambda n: n >= 1 and n <= 16, description=cleandoc( """ The subarray for which the delay polynomials apply to. Type: int Range: Integer from 1-16 inclusive """ ), ) delaymodel_schema.add_field( "station_beam_delays", [get_low_csp_station_beam_details_schema(version, strict)], ) return delaymodel_schema
[docs] def get_low_csp_station_beam_details_schema( version: str, strict: bool ) -> Schema: """ Returns the schema with the Low CSP delay details :param version: Interface version URI :param strict: Strict mode. If true, refuse even harmless schema violations (like extra keys). DO NOT USE FOR INPUT VALIDATION! :return: The JSON Schema for the command. :raise: `ValueError` exception on invalid JSON Schema URI. """ stationbeamdetail_schema = TMSchema.new( "station beam delays", version, strict, as_reference=True, ) stationbeamdetail_schema.add_field( "station_id", int, check_strict=lambda n: n >= 1 and n <= 512, description=cleandoc( """ The station ids for which the delay polynomials apply to. Type: int Range: Integer from 1-512 inclusive """ ), ) stationbeamdetail_schema.add_field( "substation_id", int, description=cleandoc( """ The substation ids for which the delay polynomials apply to. Type: int """ ), ) stationbeamdetail_schema.add_field( "xypol_coeffs_ns", [float], description=cleandoc( """ X coefficient set Delay Model is specified as coefficients for a 5th order polynomial. Coefficients of the polynomial are specified as an array. The delay at time t, where t is measured with respect the beginning of the validity interval is calculated as: d(t) = c0 + c1*t + c2*t^2 + c3*t^3 + c4*t^4 + c5*t^5 Units for coefficients c0,c1,..c5: ns/s^k where k=0,1,..5 Type: float Range for coefficients: 64 bit number """ ), ) stationbeamdetail_schema.add_field( "ypol_offset_ns", float, description=cleandoc( """ Offset for the Y polarisation Type: float """ ), ) return stationbeamdetail_schema