diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fcecc7c684ba15b7c8437b5c38b92276d70aaf6f..badf33f0d073c001e0e1616b21296912954af4be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -553,7 +553,6 @@ unit_test: - tangostationcontrol/.coverage integration_test_docker: stage: integration-tests - allow_failure: true image: docker:latest tags: - privileged diff --git a/CDB/integrations/recvcluster_ConfigDb.json b/CDB/integrations/recvcluster_ConfigDb.json index 4aecaea4862f8a9911c2832aa2ac6939f730ab7f..23659cb18c4dea5badc2c5c4ff5201aba94cde36 100644 --- a/CDB/integrations/recvcluster_ConfigDb.json +++ b/CDB/integrations/recvcluster_ConfigDb.json @@ -14,1397 +14,449 @@ "AntennaField": { "STAT/AntennaField/1": { "properties": { - "Antenna_Field_Reference_ITRF": [ - "3826577.066", - "461022.948", - "5064892.786" + "Antenna_Field_Reference_ETRS": [ + "3826896.631", "460979.131", "5064657.943" ], - "Antenna_Reference_itrf": [ - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786" + "Antenna_Reference_ETRS": [ + "3826886.142", "460980.772", "5064665.668", + "3826887.237", "460985.643", "5064664.406", + "3826889.022", "460974.271", "5064664.094", + "3826890.118", "460979.142", "5064662.831", + "3826891.214", "460984.012", "5064661.568", + "3826892.311", "460988.883", "5064660.305", + "3826891.902", "460967.768", "5064662.520", + "3826892.999", "460972.640", "5064661.256", + "3826894.094", "460977.511", "5064659.994", + "3826895.191", "460982.382", "5064658.730", + "3826896.287", "460987.252", "5064657.467", + "3826897.383", "460992.123", "5064656.204", + "3826895.879", "460966.138", "5064659.682", + "3826896.974", "460971.009", "5064658.419", + "3826898.071", "460975.880", "5064657.156", + "3826899.167", "460980.751", "5064655.893", + "3826900.263", "460985.622", "5064654.630", + "3826901.359", "460990.493", "5064653.367", + "3826900.951", "460969.378", "5064655.582", + "3826902.048", "460974.250", "5064654.319", + "3826903.143", "460979.120", "5064653.056", + "3826904.240", "460983.991", "5064651.793", + "3826906.024", "460972.619", "5064651.481", + "3826907.120", "460977.490", "5064650.218", + "3826969.290", "460898.914", "5064610.654", + "3826970.386", "460903.785", "5064609.392", + "3826972.171", "460892.413", "5064609.080", + "3826973.267", "460897.284", "5064607.817", + "3826974.363", "460902.154", "5064606.554", + "3826975.459", "460907.025", "5064605.291", + "3826975.051", "460885.911", "5064607.506", + "3826976.147", "460890.782", "5064606.242", + "3826977.243", "460895.653", "5064604.980", + "3826978.339", "460900.524", "5064603.716", + "3826979.436", "460905.394", "5064602.453", + "3826980.532", "460910.265", "5064601.190", + "3826979.027", "460884.281", "5064604.668", + "3826980.123", "460889.152", "5064603.405", + "3826981.220", "460894.022", "5064602.142", + "3826982.316", "460898.893", "5064600.879", + "3826983.412", "460903.764", "5064599.616", + "3826984.508", "460908.635", "5064598.353", + "3826984.100", "460887.521", "5064600.568", + "3826985.196", "460892.392", "5064599.305", + "3826986.292", "460897.262", "5064598.042", + "3826987.388", "460902.133", "5064596.779", + "3826989.173", "460890.761", "5064596.467", + "3826990.269", "460895.632", "5064595.204" ], - "HBAT_antenna_itrf_offsets": [ - "-1.847", - "-1.180", - " 1.493", - "-1.581", - " 0.003", - " 1.186", - "-1.315", - " 1.185", - " 0.880", - "-1.049", - " 2.367", - " 0.573", - "-0.882", - "-1.575", - " 0.804", - "-0.616", - "-0.393", - " 0.498", - "-0.350", - " 0.789", - " 0.191", - "-0.083", - " 1.971", - "-0.116", - " 0.083", - "-1.971", - " 0.116", - " 0.350", - "-0.789", - "-0.191", - " 0.616", - " 0.393", - "-0.498", - " 0.882", - " 1.575", - "-0.804", - " 1.049", - "-2.367", - "-0.573", - " 1.315", - "-1.185", - "-0.880", - " 1.581", - "-0.003", - "-1.186", - " 1.847", - " 1.180", - "-1.493" + "HBAT_PQR_rotation_angles_deg": [ + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24" + ], + "HBAT_PQR_to_ETRS_rotation_matrix": [ + "-0.1195951054", "-0.7919544517", "0.5987530018", + " 0.9928227484", "-0.0954186800", "0.0720990002", + " 0.0000330969", " 0.6030782884", "0.7976820024" ] } }, "STAT/AntennaField/2": { "properties": { - "Antenna_Field_Reference_ITRF": [ - "3826577.066", - "461022.948", - "5064892.786" + "Antenna_Field_Reference_ETRS": [ + "3826896.631", "460979.131", "5064657.943" + ], + "Antenna_Reference_ETRS": [ + "3826886.142", "460980.772", "5064665.668", + "3826887.237", "460985.643", "5064664.406", + "3826889.022", "460974.271", "5064664.094", + "3826890.118", "460979.142", "5064662.831", + "3826891.214", "460984.012", "5064661.568", + "3826892.311", "460988.883", "5064660.305", + "3826891.902", "460967.768", "5064662.520", + "3826892.999", "460972.640", "5064661.256", + "3826894.094", "460977.511", "5064659.994", + "3826895.191", "460982.382", "5064658.730", + "3826896.287", "460987.252", "5064657.467", + "3826897.383", "460992.123", "5064656.204", + "3826895.879", "460966.138", "5064659.682", + "3826896.974", "460971.009", "5064658.419", + "3826898.071", "460975.880", "5064657.156", + "3826899.167", "460980.751", "5064655.893", + "3826900.263", "460985.622", "5064654.630", + "3826901.359", "460990.493", "5064653.367", + "3826900.951", "460969.378", "5064655.582", + "3826902.048", "460974.250", "5064654.319", + "3826903.143", "460979.120", "5064653.056", + "3826904.240", "460983.991", "5064651.793", + "3826906.024", "460972.619", "5064651.481", + "3826907.120", "460977.490", "5064650.218", + "3826969.290", "460898.914", "5064610.654", + "3826970.386", "460903.785", "5064609.392", + "3826972.171", "460892.413", "5064609.080", + "3826973.267", "460897.284", "5064607.817", + "3826974.363", "460902.154", "5064606.554", + "3826975.459", "460907.025", "5064605.291", + "3826975.051", "460885.911", "5064607.506", + "3826976.147", "460890.782", "5064606.242", + "3826977.243", "460895.653", "5064604.980", + "3826978.339", "460900.524", "5064603.716", + "3826979.436", "460905.394", "5064602.453", + "3826980.532", "460910.265", "5064601.190", + "3826979.027", "460884.281", "5064604.668", + "3826980.123", "460889.152", "5064603.405", + "3826981.220", "460894.022", "5064602.142", + "3826982.316", "460898.893", "5064600.879", + "3826983.412", "460903.764", "5064599.616", + "3826984.508", "460908.635", "5064598.353", + "3826984.100", "460887.521", "5064600.568", + "3826985.196", "460892.392", "5064599.305", + "3826986.292", "460897.262", "5064598.042", + "3826987.388", "460902.133", "5064596.779", + "3826989.173", "460890.761", "5064596.467", + "3826990.269", "460895.632", "5064595.204" ], - "Antenna_Reference_itrf": [ - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786" + "HBAT_PQR_rotation_angles_deg": [ + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24" ], - "HBAT_antenna_itrf_offsets": [ - "-1.847", - "-1.180", - " 1.493", - "-1.581", - " 0.003", - " 1.186", - "-1.315", - " 1.185", - " 0.880", - "-1.049", - " 2.367", - " 0.573", - "-0.882", - "-1.575", - " 0.804", - "-0.616", - "-0.393", - " 0.498", - "-0.350", - " 0.789", - " 0.191", - "-0.083", - " 1.971", - "-0.116", - " 0.083", - "-1.971", - " 0.116", - " 0.350", - "-0.789", - "-0.191", - " 0.616", - " 0.393", - "-0.498", - " 0.882", - " 1.575", - "-0.804", - " 1.049", - "-2.367", - "-0.573", - " 1.315", - "-1.185", - "-0.880", - " 1.581", - "-0.003", - "-1.186", - " 1.847", - " 1.180", - "-1.493" + "HBAT_PQR_to_ETRS_rotation_matrix": [ + "-0.1195951054", "-0.7919544517", "0.5987530018", + " 0.9928227484", "-0.0954186800", "0.0720990002", + " 0.0000330969", " 0.6030782884", "0.7976820024" ] } }, "STAT/AntennaField/3": { "properties": { - "Antenna_Field_Reference_ITRF": [ - "3826577.066", - "461022.948", - "5064892.786" + "Antenna_Field_Reference_ETRS": [ + "3826896.631", "460979.131", "5064657.943" ], - "Antenna_Reference_itrf": [ - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786" + "Antenna_Reference_ETRS": [ + "3826886.142", "460980.772", "5064665.668", + "3826887.237", "460985.643", "5064664.406", + "3826889.022", "460974.271", "5064664.094", + "3826890.118", "460979.142", "5064662.831", + "3826891.214", "460984.012", "5064661.568", + "3826892.311", "460988.883", "5064660.305", + "3826891.902", "460967.768", "5064662.520", + "3826892.999", "460972.640", "5064661.256", + "3826894.094", "460977.511", "5064659.994", + "3826895.191", "460982.382", "5064658.730", + "3826896.287", "460987.252", "5064657.467", + "3826897.383", "460992.123", "5064656.204", + "3826895.879", "460966.138", "5064659.682", + "3826896.974", "460971.009", "5064658.419", + "3826898.071", "460975.880", "5064657.156", + "3826899.167", "460980.751", "5064655.893", + "3826900.263", "460985.622", "5064654.630", + "3826901.359", "460990.493", "5064653.367", + "3826900.951", "460969.378", "5064655.582", + "3826902.048", "460974.250", "5064654.319", + "3826903.143", "460979.120", "5064653.056", + "3826904.240", "460983.991", "5064651.793", + "3826906.024", "460972.619", "5064651.481", + "3826907.120", "460977.490", "5064650.218", + "3826969.290", "460898.914", "5064610.654", + "3826970.386", "460903.785", "5064609.392", + "3826972.171", "460892.413", "5064609.080", + "3826973.267", "460897.284", "5064607.817", + "3826974.363", "460902.154", "5064606.554", + "3826975.459", "460907.025", "5064605.291", + "3826975.051", "460885.911", "5064607.506", + "3826976.147", "460890.782", "5064606.242", + "3826977.243", "460895.653", "5064604.980", + "3826978.339", "460900.524", "5064603.716", + "3826979.436", "460905.394", "5064602.453", + "3826980.532", "460910.265", "5064601.190", + "3826979.027", "460884.281", "5064604.668", + "3826980.123", "460889.152", "5064603.405", + "3826981.220", "460894.022", "5064602.142", + "3826982.316", "460898.893", "5064600.879", + "3826983.412", "460903.764", "5064599.616", + "3826984.508", "460908.635", "5064598.353", + "3826984.100", "460887.521", "5064600.568", + "3826985.196", "460892.392", "5064599.305", + "3826986.292", "460897.262", "5064598.042", + "3826987.388", "460902.133", "5064596.779", + "3826989.173", "460890.761", "5064596.467", + "3826990.269", "460895.632", "5064595.204" ], - "HBAT_antenna_itrf_offsets": [ - "-1.847", - "-1.180", - " 1.493", - "-1.581", - " 0.003", - " 1.186", - "-1.315", - " 1.185", - " 0.880", - "-1.049", - " 2.367", - " 0.573", - "-0.882", - "-1.575", - " 0.804", - "-0.616", - "-0.393", - " 0.498", - "-0.350", - " 0.789", - " 0.191", - "-0.083", - " 1.971", - "-0.116", - " 0.083", - "-1.971", - " 0.116", - " 0.350", - "-0.789", - "-0.191", - " 0.616", - " 0.393", - "-0.498", - " 0.882", - " 1.575", - "-0.804", - " 1.049", - "-2.367", - "-0.573", - " 1.315", - "-1.185", - "-0.880", - " 1.581", - "-0.003", - "-1.186", - " 1.847", - " 1.180", - "-1.493" + "HBAT_PQR_rotation_angles_deg": [ + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24" + ], + "HBAT_PQR_to_ETRS_rotation_matrix": [ + "-0.1195951054", "-0.7919544517", "0.5987530018", + " 0.9928227484", "-0.0954186800", "0.0720990002", + " 0.0000330969", " 0.6030782884", "0.7976820024" ] } }, "STAT/AntennaField/4": { "properties": { - "Antenna_Field_Reference_ITRF": [ - "3826577.066", - "461022.948", - "5064892.786" + "Antenna_Field_Reference_ETRS": [ + "3826896.631", "460979.131", "5064657.943" + ], + "Antenna_Reference_ETRS": [ + "3826886.142", "460980.772", "5064665.668", + "3826887.237", "460985.643", "5064664.406", + "3826889.022", "460974.271", "5064664.094", + "3826890.118", "460979.142", "5064662.831", + "3826891.214", "460984.012", "5064661.568", + "3826892.311", "460988.883", "5064660.305", + "3826891.902", "460967.768", "5064662.520", + "3826892.999", "460972.640", "5064661.256", + "3826894.094", "460977.511", "5064659.994", + "3826895.191", "460982.382", "5064658.730", + "3826896.287", "460987.252", "5064657.467", + "3826897.383", "460992.123", "5064656.204", + "3826895.879", "460966.138", "5064659.682", + "3826896.974", "460971.009", "5064658.419", + "3826898.071", "460975.880", "5064657.156", + "3826899.167", "460980.751", "5064655.893", + "3826900.263", "460985.622", "5064654.630", + "3826901.359", "460990.493", "5064653.367", + "3826900.951", "460969.378", "5064655.582", + "3826902.048", "460974.250", "5064654.319", + "3826903.143", "460979.120", "5064653.056", + "3826904.240", "460983.991", "5064651.793", + "3826906.024", "460972.619", "5064651.481", + "3826907.120", "460977.490", "5064650.218", + "3826969.290", "460898.914", "5064610.654", + "3826970.386", "460903.785", "5064609.392", + "3826972.171", "460892.413", "5064609.080", + "3826973.267", "460897.284", "5064607.817", + "3826974.363", "460902.154", "5064606.554", + "3826975.459", "460907.025", "5064605.291", + "3826975.051", "460885.911", "5064607.506", + "3826976.147", "460890.782", "5064606.242", + "3826977.243", "460895.653", "5064604.980", + "3826978.339", "460900.524", "5064603.716", + "3826979.436", "460905.394", "5064602.453", + "3826980.532", "460910.265", "5064601.190", + "3826979.027", "460884.281", "5064604.668", + "3826980.123", "460889.152", "5064603.405", + "3826981.220", "460894.022", "5064602.142", + "3826982.316", "460898.893", "5064600.879", + "3826983.412", "460903.764", "5064599.616", + "3826984.508", "460908.635", "5064598.353", + "3826984.100", "460887.521", "5064600.568", + "3826985.196", "460892.392", "5064599.305", + "3826986.292", "460897.262", "5064598.042", + "3826987.388", "460902.133", "5064596.779", + "3826989.173", "460890.761", "5064596.467", + "3826990.269", "460895.632", "5064595.204" ], - "Antenna_Reference_itrf": [ - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786", - "3826577.066", - "461022.948", - "5064892.786" + "HBAT_PQR_rotation_angles_deg": [ + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24", + "24" ], - "HBAT_antenna_itrf_offsets": [ - "-1.847", - "-1.180", - " 1.493", - "-1.581", - " 0.003", - " 1.186", - "-1.315", - " 1.185", - " 0.880", - "-1.049", - " 2.367", - " 0.573", - "-0.882", - "-1.575", - " 0.804", - "-0.616", - "-0.393", - " 0.498", - "-0.350", - " 0.789", - " 0.191", - "-0.083", - " 1.971", - "-0.116", - " 0.083", - "-1.971", - " 0.116", - " 0.350", - "-0.789", - "-0.191", - " 0.616", - " 0.393", - "-0.498", - " 0.882", - " 1.575", - "-0.804", - " 1.049", - "-2.367", - "-0.573", - " 1.315", - "-1.185", - "-0.880", - " 1.581", - "-0.003", - "-1.186", - " 1.847", - " 1.180", - "-1.493" + "HBAT_PQR_to_ETRS_rotation_matrix": [ + "-0.1195951054", "-0.7919544517", "0.5987530018", + " 0.9928227484", "-0.0954186800", "0.0720990002", + " 0.0000330969", " 0.6030782884", "0.7976820024" ] } } diff --git a/docker-compose/Makefile b/docker-compose/Makefile index 7606e94abd344a9c255463664bf2f55d8e556b93..5cdb54531b472901dbda22902937e62a0ab2902b 100644 --- a/docker-compose/Makefile +++ b/docker-compose/Makefile @@ -172,10 +172,10 @@ up: minimal ## start the base TANGO system and prepare requested services $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) up --no-start --no-recreate $(SERVICE) run: minimal ## run a service using arguments and delete it afterwards - $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) run --no-deps --rm $(SERVICE) $(SERVICE_ARGS) + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) run -T --no-deps --rm $(SERVICE) $(SERVICE_ARGS) integration: minimal ## run a service using arguments and delete it afterwards - $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) run --no-deps --rm integration-test $(INTEGRATION_ARGS) + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) run -T --no-deps --rm integration-test $(INTEGRATION_ARGS) down: ## stop all services and tear down the system $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) down diff --git a/tangostationcontrol/docs/source/broken_hardware.rst b/tangostationcontrol/docs/source/broken_hardware.rst new file mode 100644 index 0000000000000000000000000000000000000000..7047d324ed7085122225a6e07ca67e830835b876 --- /dev/null +++ b/tangostationcontrol/docs/source/broken_hardware.rst @@ -0,0 +1,47 @@ +Broken Hardware +-------------------- + +Not all hardware is always functional. Broken hardware must be excluded from the signal chain, and in some cases prevented from powering up. + +Disabling antennas +`````````````````````````` + +Not all antennas present in the field are to be used. The AntennaField device exposes the following properties for each of its antennas: + +:Antenna_Quality: The condition of the antenna: 0=OK, 1=SUSPICIOUS, 2=BROKEN, 3=BEYOND_REPAIR. + + :type: ``int32[]`` + +:Antenna_Use: Whether each antenna should be used: 0=AUTO, 1=ON, 2=OFF. In AUTO mode, an antenna is used if its quality is OK or SUSPICIOUS. In ON mode, it is always used. In OFF mode, never. + + :type: ``int32[]`` + +which can also be queried as ``Antenna_Quality_R`` and ``Antenna_Use_R``. + +.. note:: If these properties are updated, you should restart both the AntennaField and DigitalBeam device to propagate their effects. + +The above settings result in a subset of the antennas in the AntennaField to be marked as usable. The following property exposes this conclusion: + +:Antenna_Usage_Mask_R: Whether antennas will be used, according to their configured state and quality. Antennas which are configured to be BROKEN, BEYOND_REPAIR, or OFF, are not used. + + :type: ``bool[N_tiles]`` + +Effect on signal chain +"""""""""""""""""""""""""" + +The DigitalBeam device will only beamform inputs that are enabled in the ``AntennaField.Antenna_Usage_Mask_R`` attribute. + + +Power distribution +-------------------------- + +At boot, during hardware initialisation, the following devices toggle power: + +* The RECV device turns all RCUs enabled in ``RCU_mask_RW`` OFF and ON, +* The RECV device powers its antennas according to its ``RCU_PWR_ANT_on_RW_default`` property, +* The AntennaField device powers its antennas, if they are: + * Enabled in ``Antenna_Usage_Mask_R`` attribute, that is, not marked as BROKEN, BEYOND_REPAIR, or OFF, + * Enabled in the ``Antenna_Needs_Power`` property. + +.. note:: Exotic inputs like a noise source must not receive power, even when used. Use the ``Antenna_Needs_Power`` property to configure which antennas should be powered on. + diff --git a/tangostationcontrol/docs/source/devices/antennafield.rst b/tangostationcontrol/docs/source/devices/antennafield.rst index bcf93f62e9d40aa4d48e24c68eb357433216542d..faede195aec38828f2c1d1d6b71d2bdb928ef6eb 100644 --- a/tangostationcontrol/docs/source/devices/antennafield.rst +++ b/tangostationcontrol/docs/source/devices/antennafield.rst @@ -46,13 +46,19 @@ The antennas represented by the antenna field are selected by the following prop :type: ``str[]`` -:Power_to_RECV_mapping: Pairs of numbers ``(recv_idx, ant_idx)`` describing the inputs on which the Antenna *power* is connected. The ``recv_idx`` is the index in ``RECV_devices``, starting at 1. The ``ant_idx`` is the absolute index of the antenna in the ``RECV`` device. A value of ``-1`` means the antenna is not connected at all. +Antenna mapping +"""""""""""""""""""" + +These properties configure which inputs in RECV represent the power and control for each antenna: + +:HBAT_Power_to_RECV_mapping: Pairs of numbers ``(recv_idx, ant_idx)`` describing the inputs on which the HBAT *power* is connected. The ``recv_idx`` is the index in ``RECV_devices``, starting at 1. The ``ant_idx`` is the absolute index of the antenna in the ``RECV`` device. A value of ``-1`` means the antenna is not connected at all. :type: ``int32[]`` :shape: ``int32[][2]`` :Control_to_RECV_mapping: Pairs of numbers ``(recv_idx, ant_idx)`` describing the inputs on which the Antenna *control* is connected. The ``recv_idx`` is the index in ``RECV_devices``, starting at 1. The ``ant_idx`` is the absolute index of the antenna in the ``RECV`` device. A value of ``-1`` means the antenna is not connected at all. + Positions """""""""""""""""""" diff --git a/tangostationcontrol/docs/source/devices/using.rst b/tangostationcontrol/docs/source/devices/using.rst index 053d73bda33703c187f6bd9de9d7d84194647254..57d2f255ed2fdd673e8996492a8b2ff434960387 100644 --- a/tangostationcontrol/docs/source/devices/using.rst +++ b/tangostationcontrol/docs/source/devices/using.rst @@ -109,12 +109,10 @@ Most devices provide the following commands, in order to configure the hardware :set_translator_defaults(): Select the hardware to configure and monitor. -:prepare_hardware(): For devices that control hardware, this command prepares the hardware to accept commands (f.e. load firmware). +:prepare_hardware(): For devices that control hardware, this command prepares the hardware to accept commands (f.e. power cycle, load firmware). :set_defaults(): Upload default attribute settings from the TangoDB to the hardware. -:initialise_hardware(): For devices that control hardware, this command runs the hardware initialisation procedure. - :on(): Mark the device as operational. Moves from ``STANDBY`` to ``ON``. See also :ref:`boot`, which provides functionality to initialise all the devices. diff --git a/tangostationcontrol/docs/source/index.rst b/tangostationcontrol/docs/source/index.rst index 6bd156c2b7f9d3cda99ad3af8a7e01bbc33c45c8..4c56206dfce3ab37b07f103a963d06846ccd8f12 100644 --- a/tangostationcontrol/docs/source/index.rst +++ b/tangostationcontrol/docs/source/index.rst @@ -34,6 +34,7 @@ Even without having access to any LOFAR2.0 hardware, you can install the full st devices/configure configure_station signal_chain + broken_hardware developer faq diff --git a/tangostationcontrol/tangostationcontrol/devices/antennafield.py b/tangostationcontrol/tangostationcontrol/devices/antennafield.py index 25b58719c287c4a6fe6e4b690f0228187ac17989..565f979539a3a70f538f39f880cd7efeae0102e4 100644 --- a/tangostationcontrol/tangostationcontrol/devices/antennafield.py +++ b/tangostationcontrol/tangostationcontrol/devices/antennafield.py @@ -110,6 +110,15 @@ class AntennaField(lofar_device): default_value = numpy.array([AntennaUse.AUTO] * MAX_NUMBER_OF_HBAT) ) + # ----- Antenna properties + + Antenna_Needs_Power = device_property( + doc="Whether to provide power to each antenna (False for noise sources)", + dtype='DevVarBooleanArray', + mandatory=False, + default_value = numpy.array([False] * MAX_NUMBER_OF_HBAT) + ) + # ----- Position information Antenna_Field_Reference_ITRF = device_property( @@ -149,7 +158,6 @@ class AntennaField(lofar_device): mandatory=False, default_value = 2015.5 ) - HBAT_PQR_rotation_angles_deg = device_property( doc='Rotation of each tile in the PQ plane ("horizontal") in degrees.', dtype='DevVarFloatArray', @@ -206,14 +214,21 @@ class AntennaField(lofar_device): Antenna_Names_R = attribute(access=AttrWriteType.READ, dtype=(str,), max_dim_x=MAX_NUMBER_OF_HBAT) - Antenna_Quality_R = attribute(access=AttrWriteType.READ, - dtype=(numpy.uint16,), max_dim_x=MAX_NUMBER_OF_HBAT) - Antenna_Use_R = attribute(access=AttrWriteType.READ, - dtype=(numpy.uint16,), max_dim_x=MAX_NUMBER_OF_HBAT) - Antenna_Usage_Mask_R = attribute(access=AttrWriteType.READ, + Antenna_Quality_R = attribute(doc='The quality of each antenna. 0=OK, 1=SUSPICIOUS, 2=BROKEN, 3=BEYOND_REPAIR.', + dtype=(numpy.uint32,), max_dim_x=MAX_NUMBER_OF_HBAT) + Antenna_Use_R = attribute(doc='Whether each antenna should be used. 0=AUTO, 1=ON, 2=OFF. In AUTO mode, the antenna is used if it is not BROKEN or BEYOND_REPAIR.', + dtype=(numpy.uint32,), max_dim_x=MAX_NUMBER_OF_HBAT) + Antenna_Quality_str_R = attribute(doc='The quality of each antenna, as a string.', + dtype=(str,), max_dim_x=MAX_NUMBER_OF_HBAT) + Antenna_Use_str_R = attribute(doc='Whether each antenna should be used, as a string.', + dtype=(str,), max_dim_x=MAX_NUMBER_OF_HBAT) + + Antenna_Usage_Mask_R = attribute(doc='Whether each antenna will be used.', dtype=(bool,), max_dim_x=MAX_NUMBER_OF_HBAT) - ANT_mask_RW = mapped_attribute("ANT_mask_RW", dtype=(bool,), max_dim_x=MAX_NUMBER_OF_HBAT, access=AttrWriteType.READ_WRITE) + ANT_mask_RW = mapped_attribute("ANT_mask_RW", dtype=(bool,), max_dim_x=MAX_NUMBER_OF_HBAT, access=AttrWriteType.READ_WRITE) + RCU_PWR_ANT_on_R = mapped_attribute("RCU_PWR_ANT_on_R", dtype=(bool,), max_dim_x=MAX_NUMBER_OF_HBAT) + RCU_PWR_ANT_on_RW = mapped_attribute("RCU_PWR_ANT_on_RW", dtype=(bool,), max_dim_x=MAX_NUMBER_OF_HBAT, access=AttrWriteType.READ_WRITE) HBAT_BF_delay_steps_R = mapped_attribute("HBAT_BF_delay_steps_R", dtype=((numpy.int64,),), max_dim_x=NUMBER_OF_ELEMENTS_PER_TILE * 2, max_dim_y=MAX_NUMBER_OF_HBAT) HBAT_BF_delay_steps_RW = mapped_attribute("HBAT_BF_delay_steps_RW", dtype=((numpy.int64,),), max_dim_x=NUMBER_OF_ELEMENTS_PER_TILE * 2, max_dim_y=MAX_NUMBER_OF_HBAT, access=AttrWriteType.READ_WRITE) HBAT_LED_on_R = mapped_attribute("HBAT_LED_on_R", dtype=((bool,),), max_dim_x=NUMBER_OF_ELEMENTS_PER_TILE * 2, max_dim_y=MAX_NUMBER_OF_HBAT) @@ -266,19 +281,29 @@ class AntennaField(lofar_device): def read_Antenna_Quality_R(self): return self.Antenna_Quality + + def read_Antenna_Use_str_R(self): + return [AntennaUse(x).name for x in self.Antenna_Use] + def read_Antenna_Quality_str_R(self): + return [AntennaQuality(x).name for x in self.Antenna_Quality] + def read_Antenna_Usage_Mask_R(self): - antenna_usage = numpy.zeros(MAX_NUMBER_OF_HBAT, dtype=bool) - for n in range(0, MAX_NUMBER_OF_HBAT): - antenna_usage[n] = (self.read_attribute('Antenna_Use_R')[n] == AntennaUse.ON - or (self.read_attribute('Antenna_Use_R')[n] == AntennaUse.AUTO and self.read_attribute('Antenna_Quality_R')[n] <= AntennaQuality.SUSPICIOUS)) - return antenna_usage + use = numpy.array(self.Antenna_Use) + quality = numpy.array(self.Antenna_Quality) + + antennas_forced_on = use == AntennaUse.ON + antennas_auto_on = numpy.logical_and(use == AntennaUse.AUTO, quality <= AntennaQuality.SUSPICIOUS) + + return numpy.logical_or(antennas_forced_on, antennas_auto_on) def read_nr_antennas_R(self): # The number of antennas should be equal to: # * the number of elements in the Control_to_RECV_mapping (after reshaping), # * the number of elements in the Power_to_RECV_mapping (after reshaping), # * the number of antennas exposed through Antenna_Reference_ITRF_R. + # * the number of elements in Antenna_Use + # * the number of elements in Antenna_Quality # # Parsing a property here is quickest, so we chose that. return len(self.Control_to_RECV_mapping) // 2 @@ -388,31 +413,15 @@ class AntennaField(lofar_device): @log_exceptions() def _prepare_hardware(self): - # Initialise the RCU hardware. - for recv_proxy in self.recv_proxies: - RCU_mask = recv_proxy.RCU_mask_RW - # Set the mask to all Trues - recv_proxy.RCU_mask_RW = [True] * 32 - # Turn off the RCUs - recv_proxy.RCU_off() - - # TODO(Stefano): restore wait attribute - #recv_proxy.wait_attribute("RECVTR_translator_busy_R", False, recv_proxy.RCU_On_Off_timeout) - - # Restore the mask - recv_proxy.RCU_mask_RW = RCU_mask - # Turn on the RCUs - recv_proxy.RCU_on() + usage_mask = self.read_attribute('Antenna_Usage_Mask_R') - # TODO(Stefano): restore wait attribute - #recv_proxy.wait_attribute("RECVTR_translator_busy_R", False, recv_proxy.RCU_On_Off_timeout) - - @log_exceptions() - def _initialise_hardware(self): # Disable controlling the tiles that fall outside the mask # WARN: Needed in configure_for_initialise but Tango does not allow to write attributes in INIT state self.proxy.write_attribute('ANT_mask_RW', self.read_attribute('Antenna_Usage_Mask_R')) + # Turn on power to antennas that need it (and due to the ANT_mask, that we're using) + self.proxy.write_attribute('RCU_PWR_ANT_on_RW', self.Antenna_Needs_Power) + # -------- # Commands # -------- @@ -452,6 +461,8 @@ class AntennaToRecvMapper(object): self.__number_of_receivers = number_of_receivers self.__default_value_mapping_read = { "ANT_mask_RW": numpy.full(number_of_antennas, False), + "RCU_PWR_ANT_on_R": numpy.full(number_of_antennas, False), + "RCU_PWR_ANT_on_RW": numpy.full(number_of_antennas, False), "HBAT_BF_delay_steps_R": numpy.zeros([number_of_antennas,32], dtype=numpy.int64), "HBAT_BF_delay_steps_RW": numpy.zeros([number_of_antennas,32], dtype=numpy.int64), "HBAT_LED_on_R": numpy.full((number_of_antennas,32), False), @@ -464,6 +475,7 @@ class AntennaToRecvMapper(object): } self.__default_value_mapping_write = { "ANT_mask_RW": numpy.full(96, False), + "RCU_PWR_ANT_on_RW": numpy.full(96, False), "HBAT_BF_delay_steps_RW": numpy.zeros([96,32], dtype=numpy.int64), "HBAT_LED_on_RW": numpy.full((96,32), False), "HBAT_PWR_LNA_on_RW": numpy.full((96,32), False), diff --git a/tangostationcontrol/tangostationcontrol/devices/apsct.py b/tangostationcontrol/tangostationcontrol/devices/apsct.py index 60563f1138c125c79274b1c2f5342b7df68c02e8..f2bf1fa4d7af436807fd74c9649627ffa4593bb3 100644 --- a/tangostationcontrol/tangostationcontrol/devices/apsct.py +++ b/tangostationcontrol/tangostationcontrol/devices/apsct.py @@ -52,6 +52,10 @@ class APSCT(opcua_device): default_value=10.0 ) + TRANSLATOR_DEFAULT_SETTINGS = [ + 'APSCTTR_monitor_rate_RW' + ] + # ---------- # Attributes # ---------- @@ -118,7 +122,7 @@ class APSCT(opcua_device): # overloaded functions # -------- - def _initialise_hardware(self): + def _prepare_hardware(self): """ Initialise the APSCT hardware. """ # Cycle clock diff --git a/tangostationcontrol/tangostationcontrol/devices/apspu.py b/tangostationcontrol/tangostationcontrol/devices/apspu.py index 08c43f5a4362b22bd15ab0065e243f741add98ae..70536471bf6238e009c258444956b440b2d6cf23 100644 --- a/tangostationcontrol/tangostationcontrol/devices/apspu.py +++ b/tangostationcontrol/tangostationcontrol/devices/apspu.py @@ -37,6 +37,10 @@ class APSPU(opcua_device): default_value=1 ) + TRANSLATOR_DEFAULT_SETTINGS = [ + 'APSPUTR_monitor_rate_RW' + ] + # ---------- # Attributes # ---------- diff --git a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py index 665caca7a4d0439e0a2195d9939210229b71cdb0..068c9cee675f57f8a503fe8f4036bc2e98b80433 100644 --- a/tangostationcontrol/tangostationcontrol/devices/lofar_device.py +++ b/tangostationcontrol/tangostationcontrol/devices/lofar_device.py @@ -11,14 +11,18 @@ """ -# PyTango imports -from tango.server import attribute, command, Device, DeviceMeta -from tango import AttrWriteType, DevState, DebugIt, Attribute, DeviceProxy, AttrDataFormat, DevSource, DevDouble +from collections.abc import Sequence import time import math +from typing import List + import numpy import textwrap +# PyTango imports +from tango.server import attribute, command, Device, DeviceMeta +from tango import AttrWriteType, DevState, DebugIt, Attribute, DeviceProxy, AttrDataFormat, DevSource, DevDouble + # Additional import from tangostationcontrol import __version__ as version from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper @@ -34,6 +38,14 @@ import logging logger = logging.getLogger() +# TODO(Corne): Remove this in L2SS-940 +def sequence_not_str(obj): + """Separate sequences / collections from str, byte or bytearray""" + + return (isinstance(obj, Sequence) or isinstance(obj, numpy.ndarray)) and not \ + isinstance(obj, (str, bytes, bytearray)) + + class lofar_device(Device, metaclass=DeviceMeta): """ @@ -92,6 +104,59 @@ class lofar_device(Device, metaclass=DeviceMeta): return self.get_state() in INITIALISED_STATES + # TODO(Corne): Actually implement locking in L2SS-940 + def atomic_read_modify_write_attribute( + self, values: numpy.ndarray, proxy: DeviceProxy, attribute: str, sparse=None + ): + """Atomatically read-modify-write the attribute on the given proxy""" + + current_values = proxy.read_attribute(attribute).value + self.merge_write(values, current_values, sparse) + proxy.write_attribute(attribute, values) + + # TODO(Corne): Update docstring in L2SS-940 + def merge_write( + self, merge_values: List[any], current_values: List[any], mask_or_sparse=None + ): + """Merge values as retrieved from :py:func:`~map_write` with current_values + + This method will modify the contents of merge_values. + + To be used by the :py:class:`~AntennaField` device to remove sparse fields + from mapped_values with recently retrieved current_values from RECV device. + + :param merge_values: values as retrieved from :py:func:`~map_write` + :param current_values: values retrieved from RECV device on specific attribute + :param sparse: The value to identify sparse entries + """ + + if mask_or_sparse is not None and sequence_not_str(mask_or_sparse): + self._merge_write_mask( + merge_values, current_values, mask_or_sparse + ) + else: + self._merge_write_delimiter( + merge_values, current_values, mask_or_sparse + ) + + def _merge_write_delimiter( + self, merge_values: List[any], current_values: List[any], sparse=None + ): + for idx, value in enumerate(merge_values): + if sequence_not_str(value): + self._merge_write_delimiter(merge_values[idx], current_values[idx], sparse) + elif value == sparse: + merge_values[idx] = current_values[idx] + + def _merge_write_mask( + self, merge_values: List[any], current_values: List[any], mask: List[any] + ): + for idx, value in enumerate(merge_values): + if sequence_not_str(value): + self._merge_write_mask(merge_values[idx], current_values[idx], mask[idx]) + elif not mask[idx]: + merge_values[idx] = current_values[idx] + @log_exceptions() def init_device(self): """ Instantiates the device in the OFF state. """ @@ -342,16 +407,6 @@ class lofar_device(Device, metaclass=DeviceMeta): # This is just the command version of _prepare_hardware(). self._prepare_hardware() - - @only_in_states(DEFAULT_COMMAND_STATES) - @fault_on_error() - @command() - @DebugIt() - def initialise_hardware(self): - """ Initialise the hardware after configuring it. """ - - # This is just the command version of _initialise_hardware(). - self._initialise_hardware() @only_in_states(INITIALISED_STATES) @fault_on_error() @@ -391,9 +446,6 @@ class lofar_device(Device, metaclass=DeviceMeta): # initialise settings self.set_defaults() - # powercycle backing hardware - self.initialise_hardware() - # make device available self.On() @@ -413,11 +465,7 @@ class lofar_device(Device, metaclass=DeviceMeta): self._set_defaults(self.TRANSLATOR_DEFAULT_SETTINGS) def _prepare_hardware(self): - """ Override this method to load any firmware before configuring the hardware. """ - pass - - def _initialise_hardware(self): - """ Override this method to initialise any hardware after configuring it. """ + """ Override this method to load any firmware or power cycle components before configuring the hardware. """ pass def _disable_hardware(self): diff --git a/tangostationcontrol/tangostationcontrol/devices/observation.py b/tangostationcontrol/tangostationcontrol/devices/observation.py index 05d225a4b9d62f7f7190615f4d71f5e34639ab4c..d2b22f4a91b211792b1bc9a08f32c590db6292ef 100644 --- a/tangostationcontrol/tangostationcontrol/devices/observation.py +++ b/tangostationcontrol/tangostationcontrol/devices/observation.py @@ -42,7 +42,7 @@ class Observation(lofar_device): """ NUM_MAX_HBAT = MAX_NUMBER_OF_HBAT - NUM_INPUTS = DigitalBeam.NUM_INPUTS + MAX_INPUTS = DigitalBeam.MAX_INPUTS NUM_BEAMLETS = DigitalBeam.NUM_BEAMLETS # Attributes @@ -52,7 +52,7 @@ class Observation(lofar_device): stop_time_R = attribute(dtype=numpy.float64, access=AttrWriteType.READ) antenna_mask_R = attribute(dtype=(numpy.int64,), max_dim_x=NUM_MAX_HBAT, access=AttrWriteType.READ) filter_R = attribute(dtype=numpy.str, access=AttrWriteType.READ) - saps_subband_R = attribute(dtype=((numpy.uint32,),), max_dim_x=NUM_INPUTS, max_dim_y=NUM_BEAMLETS, access=AttrWriteType.READ) + saps_subband_R = attribute(dtype=((numpy.uint32,),), max_dim_x=MAX_INPUTS, max_dim_y=NUM_BEAMLETS, access=AttrWriteType.READ) saps_pointing_R = attribute(dtype=((numpy.str,),), max_dim_x=3, max_dim_y=NUM_BEAMLETS, access=AttrWriteType.READ) tile_beam_R = attribute(dtype=(numpy.str,), max_dim_x=3, access=AttrWriteType.READ) first_beamlet_R = attribute(dtype=numpy.int64, access=AttrWriteType.READ) @@ -67,6 +67,8 @@ class Observation(lofar_device): self._observation_id = -1 self._stop_time = datetime.now() + self._num_inputs = 0 + def configure_for_initialise(self): """Load the JSON from the attribute and configure member variables""" @@ -113,6 +115,9 @@ class Observation(lofar_device): self.tilebeam_proxy = DeviceProxy(f"{util.get_ds_inst_name()}/Tilebeam/1") self.tilebeam_proxy.set_source(DevSource.DEV) + # Collect static information + self._num_inputs = self.digitalbeam_proxy.antenna_select_RW.shape[0] + logger.info( f"The observation with ID={self._observation_id} is " "configured. It will begin as soon as On() is called and it is" @@ -268,7 +273,7 @@ class Observation(lofar_device): def _apply_saps_antenna_select(self, antenna_mask:list): """ Convert an array of antenna indexes into a boolean select array""" - antenna_select = numpy.array([[False] * self.NUM_BEAMLETS] * self.NUM_INPUTS) + antenna_select = numpy.array([[False] * self.NUM_BEAMLETS] * self._num_inputs) first_beamlet = numpy.array(self.read_first_beamlet_R(), dtype=numpy.int64) for a in antenna_mask: for i in range(first_beamlet, self.NUM_BEAMLETS): diff --git a/tangostationcontrol/tangostationcontrol/devices/recv.py b/tangostationcontrol/tangostationcontrol/devices/recv.py index 037b8adb69fa2515f5a3e2dbaa4ed00c8a2ab43b..aeaa506c3d0cca422426db7316dd095bc34e7de6 100644 --- a/tangostationcontrol/tangostationcontrol/devices/recv.py +++ b/tangostationcontrol/tangostationcontrol/devices/recv.py @@ -70,7 +70,7 @@ class RECV(opcua_device): ) RCU_PWR_ANT_on_RW = device_property( - dtype='DevVarLong64Array', + dtype='DevVarBooleanArray', mandatory=False, default_value=[False] * 96 # turn power off by default in test setups, f.e. to prevent blowing up the noise sources ) @@ -83,7 +83,8 @@ class RECV(opcua_device): TRANSLATOR_DEFAULT_SETTINGS = [ 'ANT_mask_RW', - 'RCU_mask_RW' + 'RCU_mask_RW', + 'RECVTR_monitor_rate_RW' ] # ----- Timing values @@ -236,7 +237,7 @@ class RECV(opcua_device): # in positive delays regardless of the pointing direction. self.HBAT_bf_delay_offset = numpy.mean(self.HBAT_bf_delay_step_delays) - def _initialise_hardware(self): + def _prepare_hardware(self): """ Initialise the RCU hardware. """ # Cycle RCUs diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py index 79afeacfe41c314d63a59396ceae2b4f7d523c15..e77459a88e69b285c95ad2ac22948f359fe60afb 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/beamlet.py @@ -35,7 +35,7 @@ class Beamlet(opcua_device): A_PN = 6 N_POL = 2 N_BEAMLETS_CTRL = 488 - N_BEAMSETS_CTRL = 2 + N_BEAMSETS_CTRL = SDP.N_beamsets_ctrl N_POL_BF = 2 P_SUM = 2 # number of hops that the data of the stream has traveled to reach the BSN aligner on this node diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py index 8c190ddfbb48388dcc6ac10fb4dcccb97ed1ec26..a310eb5e44b10ac5b4f08048bd0a0850896fb3a2 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/digitalbeam.py @@ -37,9 +37,13 @@ class DigitalBeam(beam_device): * antennas: the elements in the AntennaField that feeds the FPGA, * inputs: the antenna slots of the FPGAs (covering both X and Y as one), * polarised_inputs: all the input slots of the FPGAs (separating X and Y). + + The antennas are drawn from an AntennaField device. Only the antennas enabled + in the AntennaField's Antenna_Usage_Mask will be used in beamforming. Those + disabled in the mask will get a weight of 0. """ - NUM_INPUTS = 96 + MAX_INPUTS = 96 NUM_BEAMLETS = 488 # ----------------- @@ -64,7 +68,7 @@ class DigitalBeam(beam_device): dtype=(numpy.int32,), doc='Which antenna of the antennafield is connected to each input. -1 if no antenna is present.', mandatory=False, - default_value = [-1] * NUM_INPUTS + default_value = [-1] * MAX_INPUTS ) # ---------- @@ -74,12 +78,19 @@ class DigitalBeam(beam_device): Duration_delays_R = attribute(access=AttrWriteType.READ, dtype=numpy.float64, fget=lambda self: self._delays.statistics["last"] or 0) - input_select_RW = attribute(doc='Selection of inputs to use for forming each beamlet', - dtype=((bool,),), max_dim_x=NUM_BEAMLETS, max_dim_y=NUM_INPUTS, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") + input_select_RW = attribute(doc='Selection of inputs to use for forming each beamlet. Allows selecting broken antennas.', + dtype=((bool,),), max_dim_x=NUM_BEAMLETS, max_dim_y=MAX_INPUTS, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") + + antenna_select_RW = attribute(doc='Selection of antennas desired to use for forming each beamlet (= a subset of input_select of the configured antennas). Unselects broken antennas.', + dtype=((bool,),), max_dim_x=NUM_BEAMLETS, max_dim_y=MAX_INPUTS, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") + + nr_inputs_R = attribute(doc='Number of configured inputs from the associated antenna field.', + dtype=numpy.uint32, fget="nr_inputs") - antenna_select_RW = attribute(doc='Selection of antennas to use for forming each beamlet (= a subset of input_select of the configured antennas)', - dtype=((bool,),), max_dim_x=NUM_BEAMLETS, max_dim_y=NUM_INPUTS, access=AttrWriteType.READ_WRITE, fisallowed="is_attribute_access_allowed") + def nr_inputs(self): + """ Return the number of configured inputs. """ + return max(self.Input_to_Antenna_Mapping) + 1 def read_input_select_RW(self): return self._input_select @@ -89,7 +100,7 @@ class DigitalBeam(beam_device): def read_antenna_select_RW(self): # select only the rows from self.__input_select for which a mapping onto antennas is defined. - antenna_select = [[False] * self.NUM_BEAMLETS] * (max(self.Input_to_Antenna_Mapping) + 1) + antenna_select = [[False] * self.NUM_BEAMLETS] * self.nr_inputs() for input_nr, antenna_nr in enumerate(self.Input_to_Antenna_Mapping): if antenna_nr >= 0: @@ -98,9 +109,18 @@ class DigitalBeam(beam_device): return antenna_select def write_antenna_select_RW(self, antennas): + # Do not use any broken antennas, even if the user requests it. This allows the user + # to select the antennas they would like to use. + antenna_usage_mask = self.antennafield_proxy.Antenna_Usage_Mask_R + for input_nr, antenna_nr in enumerate(self.Input_to_Antenna_Mapping): - if self.antennafield_proxy.Antenna_Usage_Mask_R[input_nr] and antenna_nr >= 0: - self._input_select[input_nr] = antennas[antenna_nr] + if antenna_nr >= 0: + if antenna_usage_mask[antenna_nr]: + # use antenna for the beamlets as supplied by the client + self._input_select[input_nr] = antennas[antenna_nr] + else: + # do not use antenna for any beamlet + self._input_select[input_nr] = False # ---------- # Summarising Attributes @@ -120,7 +140,6 @@ class DigitalBeam(beam_device): self.antennafield_proxy = DeviceProxy(self.AntennaField_Device) self.antennafield_proxy.set_source(DevSource.DEV) - antenna_usage_mask = self.antennafield_proxy.Antenna_Usage_Mask_R self.beamlet_proxy = DeviceProxy(self.Beamlet_Device) self.beamlet_proxy.set_source(DevSource.DEV) @@ -131,9 +150,9 @@ class DigitalBeam(beam_device): # Generate positions for all FPGA inputs. # Use reference position for any missing antennas so they always get a delay of 0 - input_itrf = numpy.array([reference_itrf] * self.NUM_INPUTS) + input_itrf = numpy.array([reference_itrf] * self.MAX_INPUTS) for input_nr, antenna_nr in enumerate(self.Input_to_Antenna_Mapping): - if antenna_usage_mask[input_nr] and antenna_nr >= 0: + if antenna_nr >= 0: input_itrf[input_nr] = antenna_itrf[antenna_nr] # a delay calculator @@ -143,9 +162,8 @@ class DigitalBeam(beam_device): self.relative_input_positions = input_itrf - reference_itrf # use all antennas in the mapping for all beamlets, unless specified otherwise - input_select_mask = numpy.logical_and(numpy.array(self.Input_to_Antenna_Mapping) >= 0, antenna_usage_mask) - input_select = numpy.repeat(input_select_mask, self.NUM_BEAMLETS).reshape(self.NUM_INPUTS, self.NUM_BEAMLETS) - self.write_input_select_RW(input_select) + self.write_input_select_RW(numpy.zeros((self.MAX_INPUTS, self.NUM_BEAMLETS), dtype=bool)) + self.write_antenna_select_RW(numpy.ones((self.nr_inputs(), self.NUM_BEAMLETS), dtype=bool)) # -------- # internal functions @@ -159,7 +177,7 @@ class DigitalBeam(beam_device): Returns delays[antenna][beamlet] """ - delays = numpy.zeros((self.NUM_INPUTS, self.NUM_BEAMLETS), dtype=numpy.float64) + delays = numpy.zeros((self.MAX_INPUTS, self.NUM_BEAMLETS), dtype=numpy.float64) d = self.delay_calculator d.set_measure_time(timestamp) @@ -173,7 +191,7 @@ class DigitalBeam(beam_device): """ Converts an array with dimensions [antenna][beamlet] -> [fpga_nr][input_nr][pol_nr][beamlet] by repeating the values for both polarisations. """ - assert arr.shape == (self.NUM_INPUTS, self.NUM_BEAMLETS) + assert arr.shape == (self.MAX_INPUTS, self.NUM_BEAMLETS) # Each antenna maps on [fpga_nr][input_nr][0] and [fpga_nr][input_nr][1], and we work # with [antenna][beamlet], so we have to interleave copies of the input array per beamlet @@ -206,9 +224,6 @@ class DigitalBeam(beam_device): beam_weights = self.beamlet_proxy.calculate_bf_weights(fpga_delays.flatten()) beam_weights = beam_weights.reshape((Beamlet.N_PN, Beamlet.A_PN * Beamlet.N_POL * Beamlet.N_BEAMLETS_CTRL)) - # Filter out unwanted antennas - beam_weights *= self._map_inputs_on_polarised_inputs(self._input_select) - return beam_weights @TimeIt() @@ -216,8 +231,13 @@ class DigitalBeam(beam_device): """ Uploads beam weights based on a given pointing direction 2D array (96 tiles x 3 parameters) """ - # Write weights to SDP - self.beamlet_proxy.FPGA_bf_weights_xx_yy_RW = beam_weights + + self.atomic_read_modify_write_attribute( + beam_weights, + self.beamlet_proxy, + "FPGA_bf_weights_xx_yy_RW", + self._map_inputs_on_polarised_inputs(self._input_select) + ) # Record where we now point to, now that we've updated the weights. # Only record pointings per beamlet, not which antennas took part diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py index 2a7ca8991f168f6bf5ba673207da6dc9dbb09817..d35701ee4821919631a3c4b791c631ca142ae4f0 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/sdp.py @@ -113,7 +113,7 @@ class SDP(opcua_device): # TODO(needs to not be statically declared as this can change depending on the station and configuration) S_pn = 12 # Number of ADC signal inputs per Processing Node (PN) FPGA. N_pn = 16 # Number of FPGAs per antenna band that is controlled via the SC - SDP interface. - N_beamsets_ctrl = 2 + N_beamsets_ctrl = 1 FPGA_firmware_version_R = attribute_wrapper(comms_annotation=["FPGA_firmware_version_R"], datatype=str, dims=(N_pn,)) FPGA_boot_image_R = attribute_wrapper(comms_annotation=["FPGA_boot_image_R"], datatype=numpy.int32, dims=(N_pn,), doc="Active FPGA image (0=factory, 1=user)") diff --git a/tangostationcontrol/tangostationcontrol/devices/unb2.py b/tangostationcontrol/tangostationcontrol/devices/unb2.py index 8ec767588ae0e9fe333411b763d47c765da20649..ba0c1364efe3af800ca0a3cf3ae7be313d1a38ac 100644 --- a/tangostationcontrol/tangostationcontrol/devices/unb2.py +++ b/tangostationcontrol/tangostationcontrol/devices/unb2.py @@ -70,7 +70,8 @@ class UNB2(opcua_device): ) TRANSLATOR_DEFAULT_SETTINGS = [ - 'UNB2_mask_RW' + 'UNB2_mask_RW', + 'UNB2TR_monitor_rate_RW' ] UNB2TR_I2C_bus_DDR4_error_R = attribute_wrapper(comms_annotation=["UNB2TR_I2C_bus_DDR4_error_R"],datatype=numpy.int64 , dims=(2,4)) @@ -198,7 +199,7 @@ class UNB2(opcua_device): # overloaded functions # -------- - def _initialise_hardware(self): + def _prepare_hardware(self): """ Initialise the UNB2 hardware. """ # Cycle UNB2s diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py index b06b60ce82f76b493450f1828bf5a31d8e8a928a..d4ba9f993a56dd3e6f20c03ef43e06e6ebfb02c4 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/devices/test_device_digitalbeam.py @@ -21,22 +21,30 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): antenna_qualities_only_second = numpy.array([AntennaQuality.BROKEN] + [AntennaQuality.OK] + [AntennaQuality.BROKEN] * 94) antenna_use_ok = numpy.array([AntennaUse.AUTO] * 96) + antennafield_iden = "STAT/AntennaField/1" + beamlet_iden = "STAT/Beamlet/1" + recv_iden = "STAT/RECV/1" + sdp_iden = "STAT/SDP/1" + def setUp(self): """Intentionally recreate the device object in each test""" super().setUp("STAT/DigitalBeam/1") + self.addCleanup(TestDeviceProxy.test_device_turn_off, self.beamlet_iden) + self.addCleanup(TestDeviceProxy.test_device_turn_off, self.recv_iden) + self.recv_proxy = self.setup_recv_proxy() self.beamlet_proxy = self.setup_beamlet_proxy() def setup_recv_proxy(self): - recv_proxy = TestDeviceProxy("STAT/RECV/1") + recv_proxy = TestDeviceProxy(self.recv_iden) recv_proxy.off() recv_proxy.warm_boot() recv_proxy.set_defaults() return recv_proxy def setup_beamlet_proxy(self): - beamlet_proxy = TestDeviceProxy("STAT/Beamlet/1") + beamlet_proxy = TestDeviceProxy(self.beamlet_iden) beamlet_proxy.off() beamlet_proxy.warm_boot() beamlet_proxy.set_defaults() @@ -44,7 +52,7 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): def setup_sdp_proxy(self): # setup SDP, on which this device depends - sdp_proxy = TestDeviceProxy("STAT/SDP/1") + sdp_proxy = TestDeviceProxy(self.sdp_iden) sdp_proxy.off() sdp_proxy.warm_boot() sdp_proxy.set_defaults() @@ -53,19 +61,24 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): def setup_antennafield_proxy(self, antenna_qualities, antenna_use): # setup AntennaField NR_TILES = 48 - antennafield_proxy = TestDeviceProxy("STAT/AntennaField/1") + antennafield_proxy = TestDeviceProxy(self.antennafield_iden) control_mapping = [[1,i] for i in range(NR_TILES)] - antennafield_proxy.put_property({"RECV_devices": ["STAT/RECV/1"], - "Control_to_RECV_mapping": numpy.array(control_mapping).flatten(), + antennafield_proxy.put_property({"RECV_devices": [self.recv_iden], + "HBAT_Control_to_RECV_mapping": numpy.array(control_mapping).flatten(), 'Antenna_Quality': antenna_qualities, 'Antenna_Use': antenna_use}) antennafield_proxy.off() antennafield_proxy.boot() return antennafield_proxy def test_pointing_to_zenith(self): + self.addCleanup(TestDeviceProxy.test_device_turn_off, self.sdp_iden) + self.addCleanup( + TestDeviceProxy.test_device_turn_off, self.antennafield_iden + ) + self.setup_antennafield_proxy(self.antenna_qualities_ok, self.antenna_use_ok) self.setup_sdp_proxy() - self.setup_recv_proxy() + # Setup beamlet configuration self.beamlet_proxy.clock_RW = 200 * 1000000 self.beamlet_proxy.subband_select = list(range(488)) @@ -75,13 +88,86 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): self.proxy.on() # Point to Zenith - self.proxy.set_pointing(numpy.array([["AZELGEO","0deg","90deg"]] * 488).flatten()) + self.proxy.set_pointing(numpy.array([["AZELGEO", "0deg", "90deg"]] * 488).flatten()) # beam weights should now be non-zero, we don't actually check their values for correctness self.assertNotEqual(0, sum(self.beamlet_proxy.FPGA_bf_weights_xx_yy_RW.flatten())) + + def test_set_pointing_masked_enable(self): + """Verify that only selected inputs are written""" + + self.addCleanup(TestDeviceProxy.test_device_turn_off, self.sdp_iden) + self.addCleanup( + TestDeviceProxy.test_device_turn_off, self.antennafield_iden + ) + + self.setup_antennafield_proxy(self.antenna_qualities_ok, self.antenna_use_ok) + self.setup_sdp_proxy() + + # Setup beamlet configuration + self.beamlet_proxy.clock_RW = 200 * 1000000 + self.beamlet_proxy.subband_select = list(range(488)) + + self.proxy.initialise() + self.proxy.Tracking_enabled_RW = False + self.proxy.on() + + all_zeros = numpy.array([[0] * 5856] * 16) + self.beamlet_proxy.FPGA_bf_weights_xx_yy_RW = all_zeros + + # Enable all inputs + self.proxy.input_select_RW = numpy.array([[True] * 488] * 96) + + self.proxy.set_pointing( + numpy.array([["AZELGEO", "0deg", "90deg"]] * 488).flatten() + ) + + # Verify all zeros are replaced with other values for all inputs + self.assertTrue(numpy.any(numpy.not_equal( + all_zeros, self.beamlet_proxy.FPGA_bf_weights_xx_yy_RW + ))) + + def test_set_pointing_masked_disable(self): + """Verify that only diabled inputs are unchanged""" + + self.addCleanup(TestDeviceProxy.test_device_turn_off, self.sdp_iden) + self.addCleanup( + TestDeviceProxy.test_device_turn_off, self.antennafield_iden + ) + + self.setup_antennafield_proxy(self.antenna_qualities_ok, self.antenna_use_ok) + self.setup_sdp_proxy() + + # Setup beamlet configuration + self.beamlet_proxy.clock_RW = 200 * 1000000 + self.beamlet_proxy.subband_select = list(range(488)) + + self.proxy.initialise() + self.proxy.Tracking_enabled_RW = False + self.proxy.on() + + non_zeros = numpy.array([[16] * 5856] * 16) + self.beamlet_proxy.FPGA_bf_weights_xx_yy_RW = non_zeros + + # Disable all inputs + self.proxy.input_select_RW = numpy.array([[False] * 488] * 96) + + self.proxy.set_pointing( + numpy.array([["AZELGEO", "0deg", "90deg"]] * 488).flatten() + ) + + # Verify all zeros are replaced with other values for all inputs + numpy.testing.assert_equal( + non_zeros, self.beamlet_proxy.FPGA_bf_weights_xx_yy_RW + ) def test_input_select_with_all_antennas_ok(self): - """ Verify if input and antenna select are correctly calculated following Antennafield.Antenna_Usage_Mask """ + """Verify if input and antenna select are correctly calculated following Antennafield.Antenna_Usage_Mask""" + + self.addCleanup( + TestDeviceProxy.test_device_turn_off, self.antennafield_iden + ) + antennafield_proxy = self.setup_antennafield_proxy(self.antenna_qualities_ok, self.antenna_use_ok) numpy.testing.assert_equal(numpy.array([True] * 96), antennafield_proxy.Antenna_Usage_Mask_R) self.setUp() @@ -92,7 +178,12 @@ class TestDeviceDigitalBeam(AbstractTestBases.TestDeviceBase): numpy.testing.assert_equal(expected_antenna_select, self.proxy.antenna_select_RW) def test_input_select_with_only_second_antenna_ok(self): - """ Verify if input and antenna select are correctly calculated following Antennafield.Antenna_Usage_Mask """ + """Verify if input and antenna select are correctly calculated following Antennafield.Antenna_Usage_Mask""" + + self.addCleanup( + TestDeviceProxy.test_device_turn_off, self.antennafield_iden + ) + antennafield_proxy = self.setup_antennafield_proxy(self.antenna_qualities_only_second, self.antenna_use_ok) numpy.testing.assert_equal(numpy.array([False] + [True] + [False] * 94), antennafield_proxy.Antenna_Usage_Mask_R) self.setUp() diff --git a/tangostationcontrol/tangostationcontrol/integration_test/default/statistics/test_writer_sst.py b/tangostationcontrol/tangostationcontrol/integration_test/default/statistics/test_writer_sst.py index d8932263df5079790e14cdd988f73c3224e8c53a..2d98e2fce13730e2e02ea49316c29fe2dd693b1f 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/default/statistics/test_writer_sst.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/default/statistics/test_writer_sst.py @@ -46,6 +46,45 @@ class TestStatisticsWriterSST(BaseIntegrationTestCase): self.assertIsNotNone(self.recv_proxy.RCU_Band_Select_R) self.assertIsNotNone(self.recv_proxy.RCU_DTH_on_R) + def test_header_info(self): + """ Test whether the header info are inserted and collected in the proper way""" + with TemporaryDirectory() as tmpdir: + new_sys_argv = [ + sys.argv[0], + "--mode", "SST", + "--no-tango", + "--file", join( + dirname(dirname(dirname(dirname(__file__)))), + "test/statistics", "SDP_SST_statistics_packets.bin" + ), + "--output_dir", tmpdir + ] + + with mock.patch.object(entry.sys, 'argv', new_sys_argv): + with self.assertRaises(SystemExit): + entry.main() + + # check if file was written + self.assertTrue(isfile(f"{tmpdir}/SST_2021-09-20-12-17-40.h5")) + + # test statistics reader + new_sys_argv = [ + sys.argv[0], + "--files", f"{tmpdir}/SST_2021-09-20-12-17-40.h5", + "--start_time", "2021-09-20#07:40:08.937+00:00", + "--end_time", "2021-10-04#07:50:08.937+00:00" + ] + with mock.patch.object(reader.sys, 'argv', new_sys_argv): + stat_parser = reader.setup_stat_parser() + SSTstatistics = stat_parser.list_statistics() + self.assertIsNotNone(SSTstatistics) + stat = stat_parser.get_statistic( + '2021-09-20T12:17:40.000+00:00' + ) + self.assertIsNotNone(stat) + self.assertEqual("0.1", stat.station_version_id) + self.assertEqual("0.1", stat.writer_version_id) + def test_insert_tango_SST_statistics(self): self.assertEqual(DevState.ON, self.recv_proxy.state()) collector = StationSSTCollector(device=self.recv_proxy) diff --git a/tangostationcontrol/tangostationcontrol/integration_test/device_proxy.py b/tangostationcontrol/tangostationcontrol/integration_test/device_proxy.py index 93f84dc98a789c2e0fa5451529728dbed2e73f79..4c519a41184e933e6d9bc720df75f494f7b87051 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/device_proxy.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/device_proxy.py @@ -22,6 +22,7 @@ class TestDeviceProxy(DeviceProxy): # See also https://www.tango-controls.org/community/forum/c/development/python/attribute-direct-reading-from-device-when-polling-is-turned-on/ self.set_source(DevSource.DEV) + @staticmethod def test_device_turn_off(endpoint): d = TestDeviceProxy(endpoint) diff --git a/tangostationcontrol/tangostationcontrol/integration_test/recv_cluster/test_recv_cluster.py b/tangostationcontrol/tangostationcontrol/integration_test/recv_cluster/test_recv_cluster.py index 0c1fdcafc848ff87d23c1ba8ddd0eb9b87b46bab..fea5e86b8b896cff43f6c38b8122b8ad2b08ac30 100644 --- a/tangostationcontrol/tangostationcontrol/integration_test/recv_cluster/test_recv_cluster.py +++ b/tangostationcontrol/tangostationcontrol/integration_test/recv_cluster/test_recv_cluster.py @@ -11,25 +11,25 @@ import time import numpy import logging import statistics -import unittest from tango import DevState from tangostationcontrol.integration_test import base from tangostationcontrol.integration_test.device_proxy import TestDeviceProxy +from tangostationcontrol.devices.antennafield import AntennaQuality, AntennaUse logger = logging.getLogger() class TestRecvCluster(base.IntegrationTestCase): - POINTING_DIRECTION = numpy.array([["J2000", "0deg", "0deg"]] * 96).flatten() + NR_TILES = 48 + POINTING_DIRECTION = numpy.array([["J2000","0deg","0deg"]] * NR_TILES).flatten() def setUp(self): super(TestRecvCluster, self).setUp() - @unittest.skip("waiting for L2SS-799") def test_recv_cluster_performance(self): beam_proxies = [] antenna_field_proxies = [] @@ -45,27 +45,31 @@ class TestRecvCluster(base.IntegrationTestCase): for proxy in recv_proxies: proxy.off() self.assertTrue(proxy.state() is DevState.OFF) - proxy.initialise() - self.assertTrue(proxy.state() is DevState.STANDBY) + proxy.warm_boot() proxy.set_defaults() - proxy.on() self.assertTrue(proxy.state() is DevState.ON) - for proxy in antenna_field_proxies: + for n in range(1, 5): + proxy = antenna_field_proxies[n-1] + # setup AntennaField + NR_TILES = 48 + control_mapping = [[1,i] for i in range(NR_TILES)] + antenna_qualities = numpy.array([AntennaQuality.OK] * 96) + antenna_use = numpy.array([AntennaUse.AUTO] * 96) + proxy.put_property({"RECV_devices": [f"STAT/RECV/{n}"], + "Control_to_RECV_mapping": numpy.array(control_mapping).flatten(), + 'Antenna_Quality': antenna_qualities, 'Antenna_Use': antenna_use}) proxy.off() - self.assertTrue(proxy.state() is DevState.OFF) - proxy.initialise() - self.assertTrue(proxy.state() is DevState.STANDBY) - proxy.set_defaults() - proxy.on() + proxy.boot() + self.assertEqual(NR_TILES, proxy.nr_antennas_R) self.assertTrue(proxy.state() is DevState.ON) for proxy in beam_proxies: proxy.off() self.assertTrue(proxy.state() is DevState.OFF) - proxy.initialise() - self.assertTrue(proxy.state() is DevState.STANDBY) - proxy.on() + proxy.warm_boot() + proxy.set_defaults() + proxy.Tracking_enabled_RW = False self.assertTrue(proxy.state() is DevState.ON) results = [] diff --git a/tangostationcontrol/tangostationcontrol/statistics/reader.py b/tangostationcontrol/tangostationcontrol/statistics/reader.py index cb9370fd206440de37f31bdbaae7218f71470726..eb1594540554b443ec232bee33ffbc67a1cb4722 100644 --- a/tangostationcontrol/tangostationcontrol/statistics/reader.py +++ b/tangostationcontrol/tangostationcontrol/statistics/reader.py @@ -156,7 +156,7 @@ class statistics_data: # we will be creating potentially tens of thousands of these object. Using __slots__ makes them faster and uses less memory. At the cost of # having to list all self attributes here. - __slots__ = ("version_id", "timestamp", "station_id", "source_info_t_adc", "source_info_subband_calibrated_flag", "source_info_payload_error", + __slots__ = ("version_id", "station_version_id", "writer_version_id", "timestamp", "station_id", "source_info_t_adc", "source_info_subband_calibrated_flag", "source_info_payload_error", "source_info_payload_error", "source_info_payload_error", "source_info_nyquist_zone_index", "source_info_gn_index", "source_info_fsub_type", "source_info_beam_repositioning_flag", "source_info_antenna_band_index", "source_info__raw", "observation_id", "nof_statistics_per_packet", "nof_signal_inputs", "nof_bytes_per_statistic", "marker", "integration_interval_raw", @@ -169,6 +169,10 @@ class statistics_data: # get all the general header info self.version_id = file[group_key].attrs["version_id"] self.station_id = file[group_key].attrs["station_id"] + + # get software version info + self.station_version_id = file[group_key].attrs["station_version_id"] + self.writer_version_id = file[group_key].attrs["writer_version_id"] # convert string timestamp to datetime object self.timestamp = datetime.datetime.fromisoformat(file[group_key].attrs["timestamp"]) diff --git a/tangostationcontrol/tangostationcontrol/statistics/writer/VERSION b/tangostationcontrol/tangostationcontrol/statistics/writer/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..49d59571fbf6e077eece30f8c418b6aad15e20b0 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/statistics/writer/VERSION @@ -0,0 +1 @@ +0.1 diff --git a/tangostationcontrol/tangostationcontrol/statistics/writer/hdf5.py b/tangostationcontrol/tangostationcontrol/statistics/writer/hdf5.py index 4ac9dd9d451ae77a93b0f5f0d848d4de138cfd8c..a22c909fd5ec461e449094003297c1adbda9e451 100644 --- a/tangostationcontrol/tangostationcontrol/statistics/writer/hdf5.py +++ b/tangostationcontrol/tangostationcontrol/statistics/writer/hdf5.py @@ -10,6 +10,7 @@ from abc import ABC, abstractmethod from datetime import datetime, timedelta import logging +import os # python hdf5 import h5py @@ -33,6 +34,19 @@ __all__ = [ "SstHdf5Writer", "BstHdf5Writer", ] +def _get_lsc_version() -> str: + """ Retrieve the Lofar Station Control software version """ + filepath = os.path.join(os.path.realpath('../'), 'tangostationcontrol/VERSION') + with open(filepath) as f: + version = f.readline().strip() + return version or "" + +def _get_writer_version() -> str: + """ Retrieve the Statistics Writer software version """ + filepath = os.path.join(os.path.dirname(__file__), 'VERSION') + with open(filepath) as f: + version = f.readline().strip() + return version or "" class HDF5Writer(ABC): SST_MODE = "SST" @@ -67,6 +81,10 @@ class HDF5Writer(ABC): # writer is in (SST,XST,BST) self.mode = statistics_mode.upper() + # software version + self.station_version_id = _get_lsc_version() + self.writer_version_id = _get_writer_version() + # Set device if any, defaults to None self.device = device @@ -207,6 +225,10 @@ class HDF5Writer(ABC): timespec="milliseconds" ) + # store software version entries + header["station_version_id"] = self.station_version_id + header["writer_version_id"] = self.writer_version_id + # Stores the header of the packet received for this matrix as a list of # attributes for k, v in header.items(): diff --git a/tangostationcontrol/tangostationcontrol/test/devices/test_digitalbeam_device.py b/tangostationcontrol/tangostationcontrol/test/devices/test_digitalbeam_device.py new file mode 100644 index 0000000000000000000000000000000000000000..0d19f7cab3b6284caed0e6a6e63f701957f5c582 --- /dev/null +++ b/tangostationcontrol/tangostationcontrol/test/devices/test_digitalbeam_device.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +# Builtin regular libraries +import copy + +# External regular libraries +import numpy + +# Internal regular imports +from tangostationcontrol.devices.sdp import digitalbeam + +# Builtin test libraries +from unittest import mock +import unittest + +# External test libraries +from tango.test_context import DeviceTestContext + +# Internal test imports +from tangostationcontrol.test.devices import device_base + + +class TestDigitalBeamDevice(device_base.DeviceTestCase): + + def setUp(self): + # DeviceTestCase setUp patches lofar_device DeviceProxy + super(TestDigitalBeamDevice, self).setUp() + + @unittest.skip("Test for manual use, enable at most one (process=false)") + @mock.patch.object(digitalbeam.DigitalBeam, "_wait_to_apply_weights") + @mock.patch.object(digitalbeam.DigitalBeam, "_compute_weights") + @mock.patch.object(digitalbeam, "DeviceProxy") + def test_apply_weights_disabled(self, m_proxy, m_compute, m_wait): + """Verify won't overwrite digitalbeam data if no input_selected""" + + input_data = numpy.array([["AZELGEO", "0deg", "90deg"]] * 488).flatten() + current_data = numpy.array([[16384] * 5856] * 16) + + m_proxy.return_value = mock.Mock( + read_attribute=mock.Mock( + return_value=mock.Mock(value=copy.copy(current_data)) + ), + Antenna_Usage_Mask_R=numpy.array([0] * 96), + Antenna_Field_Reference_ITRF_R=mock.MagicMock(), + HBAT_reference_ITRF_R=numpy.array([[0] * 3] * 96) + ) + + new_data = numpy.array( + [[16384] * 2928 + [0] * 2928] * 16 + ) + m_compute.return_value = copy.copy(new_data) + + with DeviceTestContext( + digitalbeam.DigitalBeam, process=False, + ) as proxy: + proxy.initialise() + proxy.Tracking_enabled_RW = False + proxy.input_select_RW = numpy.array([[False] * 488] * 96) + + proxy.set_pointing(input_data) + + numpy.testing.assert_equal( + m_proxy.return_value.write_attribute.call_args[0][0], + current_data + ) + + @unittest.skip("Test for manual use, enable at most one (process=false)") + @mock.patch.object(digitalbeam.DigitalBeam, "_wait_to_apply_weights") + @mock.patch.object(digitalbeam.DigitalBeam, "_compute_weights") + @mock.patch.object(digitalbeam, "DeviceProxy") + def test_apply_weights_enabled(self, m_proxy, m_compute, m_wait): + """Verify can overwrite digitalbeam data if input_selected""" + + input_data = numpy.array([["AZELGEO", "0deg", "90deg"]] * 488).flatten() + current_data = numpy.array([[16384] * 5856] * 16) + + m_proxy.return_value = mock.Mock( + read_attribute=mock.Mock( + return_value=mock.Mock(value=current_data) + ), + Antenna_Usage_Mask_R=numpy.array([0] * 96), + Antenna_Field_Reference_ITRF_R=mock.MagicMock(), + HBAT_reference_ITRF_R=numpy.array([[0] * 3] * 96) + ) + + new_data = numpy.array( + [[16384] * 2928 + [0] * 2928] * 16 + ) + m_compute.return_value = copy.copy(new_data) + + with DeviceTestContext( + digitalbeam.DigitalBeam, process=False, + ) as proxy: + proxy.initialise() + proxy.Tracking_enabled_RW = False + proxy.input_select_RW = numpy.array([[True] * 488] * 96) + + proxy.set_pointing(input_data) + + numpy.testing.assert_equal( + m_proxy.return_value.write_attribute.call_args[0][0], + new_data + )