Overview
The RcalEmulatorDevice is a Tango Device designed to provide real-time calibration (RCal)
emulator for the Square Kilometre Array (SKA) LOW CBF testing purpose.
It emulates the generation of Jones calibration matrices for multiple beams, stations, and frequencies, and streams the results with associated metadata.
This device supports configuration of calibration parameters, execution of the calibration emulator, and integration with Kafka for data transport.
Attributes
Attribute Name |
Type |
Description |
|---|---|---|
|
String |
URL and port number of the Kafka server to which calibration data is sent. |
|
Array[int] |
List of SPS frequencies used during calibration. Configurable via |
|
Array[String] |
List of stations/substations included in calibration. Configurable via |
|
Float |
Rotation angle (in degrees) applied to both X and Y polarisations. |
|
Array[Int] |
List of PST beams to be calibrated. Configurable via |
|
Float |
Period (in seconds) at which calibration matrices are generated and sent. |
|
Integer |
Max Message size proxy where Kafka message_max_bytes=max_message * 2^20. |
|
String |
Read-only JSON list of the registered output generations. Managed via |
Commands
configure_rcal
Input: JSON string
Output: Tango Result → Ok if configuration done, else FAILED.
Description:
Configures the lists of SPS frequencies, stations, and PST beams.
Example JSON:
{
"frequencies": [64, 65],
"stations": ["1/1", "2/1"],
"beams": [1, 2]
}
start_calibration
Input: Topic name
Output: Tango Result
Description:
Starts the calibration emulator. Once started, the device will generate and send
Jones matrices every updating_period seconds.
Workflow:
Prepare one Jones matrix per beam:
If
cal_count % 2 == 0→ use identity matrix.Else → apply rotation matrix with
rotate_degrees.
Attach metadata to the calibration data.
Send calibration results to Kafka as an
xarray.DataArray.
stop_calibration
Input: None
Output: Tango Result
Description:
Stops the calibration process. Allow for one full updating period before restarting.
add_rcal_generation
Input: JSON string
Output: Tango Result → Ok if the generation was registered, else FAILED.
Description:
Registers one output generation. The emulator can hold any number of
generations, each an independent typed stream with its own Kafka topic. Each
generation produces one Kafka message per beam every updating_period.
type selects the data product and defaults to "pst" when omitted:
pst— requiresbeams; publishes onegaintable per PST beam, one Kafka message per beam on<topic>_beam_<beam>(the default, backward-compatible product).pss— requirespss_beams; publishes one Kafka message per update period to a single topic (<topic>) containing astation_jonesplus thebeam_jonesfor all PSS beams.
Generations may be added while calibration is running; new ones are picked up on the next update period.
Example PST JSON:
{
"type": "pst",
"topic": "cal_pst",
"frequencies": [64, 65],
"stations": ["1/1", "2/1"],
"beams": [1, 2]
}
Example PSS JSON:
{
"type": "pss",
"topic": "cal_pss",
"frequencies": [64, 65],
"stations": ["1/1", "2/1"],
"pss_beams": [187, 188, 189],
"beam_rotations": [0, 45, 90]
}
beam_rotations is optional; when given it must have one angle (degrees) per
entry in pss_beams.
If no generation is registered, start_calibration synthesises a single
default PST generation from the configure_rcal lists and the topic passed
to it, preserving the original single-stream behaviour.
clear_rcal_generations
Input: None
Output: Tango Result
Description:
Removes all registered output generations.
Data Format
The calibration results are sent as an xarray.DataArray object:
xarray_to_return = xarray.DataArray(
data,
coords={
"antenna": list_of_stations,
"frequency": list_of_frequencies,
"polarisation": polarisations.split(","), # e.g., ["XX", "XY", "YX", "YY"]
},
attrs={
"cal_interval_start": interval_start_time,
"cal_interval_end": interval_end_time,
"cal_count": count,
},
)
datacontains the generated Jones matrices.coordsprovide mapping to beams, stations, frequencies, and polarisations.attrscarry timing and counter metadata for traceability.
PSS data format
A pss generation instead sends the whole xarray.Dataset as a single
message on one topic (<topic>). It carries a shared station_jones and a
beam_jones spanning every PSS beam, using the receptor1/receptor2
convention (values ["X", "Y"]). beam_jones follows the same
identity/rotation toggle as station_jones but with an optional per-beam
rotation angle (beam_rotations, one degree value per pss beam): on even
solutions every beam is the identity, on odd solutions beam i is rotated by
beam_rotations[i]. When beam_rotations is omitted every beam falls back
to the global rotate_degrees (station_jones always uses the global
value):
<xarray.Dataset>
Dimensions: (antenna, frequency, receptor1: 2, receptor2: 2,
pss_beam_id)
Data variables:
station_jones (antenna, frequency, receptor1, receptor2) complex64
beam_jones (pss_beam_id, frequency, receptor1, receptor2) complex64
Note
Unit tests in this repository do not connect to Kafka; they verify dataset shapes and generation registration only. A live Kafka round-trip test belongs in the integration repository, where the Kafka deployment is in scope.
Example Workflow
Configure the device:
json_config = '{"frequencies": [64, 65], "stations": ["1/1", "2/1"], "beams": [1, 2]}' rcal = tango.DeviceProxy(f"{TANGO_DB_HOST}/low-cbf/rcal/0") rcal.configure_rcal(json_config)
Adjust attributes if necessary:
rcal.rotate_degrees = 45.0 rcal.kafka_address = "test-kafka.rcal-jou008.svc.cluster.local:9092" rcal.updating_period = 10.0
Start calibration:
rcal.start_calibration("my-topic")
Receive results:
Jones matrices are received every
updating_periodseconds in the Kafka stream.
Example Workflow (multiple / PSS generations)
To publish several typed streams at once, register each with
add_rcal_generation and then start the emulator (the topic passed to
start_calibration is ignored once generations are registered):
rcal = tango.DeviceProxy(f"{TANGO_DB_HOST}/low-cbf/rcal/0")
rcal.add_rcal_generation(
'{"type": "pst", "topic": "cal_pst", "frequencies": [64, 65], '
'"stations": ["1/1", "2/1"], "beams": [1, 2]}'
)
rcal.add_rcal_generation(
'{"type": "pss", "topic": "cal_pss", "frequencies": [64, 65], '
'"stations": ["1/1", "2/1"], "pss_beams": [187, 188, 189]}'
)
rcal.start_calibration("unused")