@@ -33,87 +33,114 @@ TEST_CASE("Test current sensor") {
33
33
{MeasuredTerminalType::branch_from, MeasuredTerminalType::branch_to, MeasuredTerminalType::branch3_1,
34
34
MeasuredTerminalType::branch3_2, MeasuredTerminalType::branch3_3}) {
35
35
CAPTURE (terminal_type);
36
- // for (auto const angle_measurement_type : {AngleMeasurementType::global_angle,
37
- // AngleMeasurementType::local_angle}) {
38
- // }
39
- auto const angle_measurement_type = AngleMeasurementType::global_angle;
40
- CAPTURE (angle_measurement_type);
41
-
42
- CurrentSensorInput<symmetric_t > sym_current_sensor_input{};
43
- sym_current_sensor_input.id = 0 ;
44
- sym_current_sensor_input.measured_object = 1 ;
45
- sym_current_sensor_input.measured_terminal_type = terminal_type;
46
- sym_current_sensor_input.angle_measurement_type = angle_measurement_type;
47
- sym_current_sensor_input.i_sigma = 1.0 ;
48
- sym_current_sensor_input.i_measured = 1.0 * 1e3 ;
49
- sym_current_sensor_input.i_angle_measured = pi / 4 .;
50
- sym_current_sensor_input.i_angle_sigma = 0.2 ;
51
-
52
- double const u_rated = 10.0e3 ;
53
- double const base_current = base_power_3p / u_rated / sqrt3;
54
- double const i_pu = 1.0e3 / base_current;
55
- double const i_sigma_pu = 1.0 / base_current;
56
- double const i_variance_pu = i_sigma_pu * i_sigma_pu;
57
- double const i_angle = pi / 4 .;
58
- double const i_angle_sigma_pi = 0.2 ;
59
- double const i_angle_variance_pu = i_angle_sigma_pi * i_angle_sigma_pi;
60
-
61
- auto const i_sym = ComplexValue<symmetric_t >{(1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current};
62
- auto const i_asym = ComplexValue<asymmetric_t >{
63
- (1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current,
64
- (1e3 * cos (i_angle + deg_240) + 1i * 1e3 * sin (i_angle + deg_240)) / base_current,
65
- (1e3 * cos (i_angle + deg_120) + 1i * 1e3 * sin (i_angle + deg_120)) / base_current};
66
-
67
- CurrentSensor<symmetric_t > const sym_current_sensor{sym_current_sensor_input, u_rated};
68
-
69
- CurrentSensorCalcParam<symmetric_t > sym_sensor_param = sym_current_sensor.calc_param <symmetric_t >();
70
- CurrentSensorCalcParam<asymmetric_t > asym_sensor_param = sym_current_sensor.calc_param <asymmetric_t >();
71
-
72
- CurrentSensorOutput<symmetric_t > const sym_sensor_output =
73
- sym_current_sensor.get_output <symmetric_t >(i_sym, ComplexValue<symmetric_t >{0.0 });
74
- CurrentSensorOutput<asymmetric_t > sym_sensor_output_asym_param =
75
- sym_current_sensor.get_output <asymmetric_t >(i_asym, ComplexValue<asymmetric_t >{0.0 });
76
-
77
- // Check symmetric sensor output for symmetric parameters
78
- if constexpr (angle_measurement_type == AngleMeasurementType::global_angle) {
79
- CHECK (sym_sensor_param.angle_measurement_type == AngleMeasurementType::global_angle);
80
- } else {
81
- CHECK (sym_sensor_param.angle_measurement_type == AngleMeasurementType::local_angle);
36
+ for (auto const angle_measurement_type :
37
+ {AngleMeasurementType::global_angle, AngleMeasurementType::local_angle}) {
38
+ CAPTURE (angle_measurement_type);
39
+
40
+ CurrentSensorInput<symmetric_t > sym_current_sensor_input{};
41
+ sym_current_sensor_input.id = 0 ;
42
+ sym_current_sensor_input.measured_object = 1 ;
43
+ sym_current_sensor_input.measured_terminal_type = terminal_type;
44
+ sym_current_sensor_input.angle_measurement_type = angle_measurement_type;
45
+ sym_current_sensor_input.i_sigma = 1.0 ;
46
+ sym_current_sensor_input.i_measured = 1.0 * 1e3 ;
47
+ sym_current_sensor_input.i_angle_measured = pi / 4 .;
48
+ sym_current_sensor_input.i_angle_sigma = 0.2 ;
49
+
50
+ double const u_rated = 10.0e3 ;
51
+ double const base_current = base_power_3p / u_rated / sqrt3;
52
+ double const i_pu = 1.0e3 / base_current;
53
+ double const i_sigma_pu = 1.0 / base_current;
54
+ double const i_variance_pu = i_sigma_pu * i_sigma_pu;
55
+ double const i_angle = pi / 4 .;
56
+ double const i_angle_sigma_pi = 0.2 ;
57
+ double const i_angle_variance_pu = i_angle_sigma_pi * i_angle_sigma_pi;
58
+
59
+ auto const i_sym =
60
+ ComplexValue<symmetric_t >{(1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current};
61
+ auto const i_asym = ComplexValue<asymmetric_t >{
62
+ (1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current,
63
+ (1e3 * cos (i_angle + deg_240) + 1i * 1e3 * sin (i_angle + deg_240)) / base_current,
64
+ (1e3 * cos (i_angle + deg_120) + 1i * 1e3 * sin (i_angle + deg_120)) / base_current};
65
+ auto const i_asym_local =
66
+ ComplexValue<asymmetric_t >{(1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current,
67
+ (1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current,
68
+ (1e3 * cos (i_angle) + 1i * 1e3 * sin (i_angle)) / base_current};
69
+
70
+ CurrentSensor<symmetric_t > const sym_current_sensor{sym_current_sensor_input, u_rated};
71
+
72
+ CurrentSensorCalcParam<symmetric_t > sym_sensor_param = sym_current_sensor.calc_param <symmetric_t >();
73
+ CurrentSensorCalcParam<asymmetric_t > asym_sensor_param = sym_current_sensor.calc_param <asymmetric_t >();
74
+
75
+ CurrentSensorOutput<symmetric_t > const sym_sensor_output =
76
+ sym_current_sensor.get_output <symmetric_t >(i_sym, ComplexValue<symmetric_t >{1.0 });
77
+ CurrentSensorOutput<asymmetric_t > sym_sensor_output_asym_param =
78
+ sym_current_sensor.get_output <asymmetric_t >(i_asym, ComplexValue<asymmetric_t >{1.0 });
79
+
80
+ // These two are only to test the residuals for local angle measurements.
81
+ // conj(i) simulates the phase shift for local angle output residuals when the reference voltage is
82
+ // real.
83
+ CurrentSensorOutput<symmetric_t > const sym_sensor_output_local_residuals =
84
+ sym_current_sensor.get_output <symmetric_t >(conj (i_sym), ComplexValue<symmetric_t >{1.0 });
85
+ CurrentSensorOutput<asymmetric_t > sym_sensor_output_asym_param_local_residuals =
86
+ sym_current_sensor.get_output <asymmetric_t >(conj (i_asym_local), ComplexValue<asymmetric_t >{1.0 });
87
+
88
+ // Check symmetric sensor output for symmetric parameters
89
+ if (angle_measurement_type == AngleMeasurementType::global_angle) {
90
+ CHECK (sym_sensor_param.angle_measurement_type == AngleMeasurementType::global_angle);
91
+ } else {
92
+ CHECK (sym_sensor_param.angle_measurement_type == AngleMeasurementType::local_angle);
93
+ }
94
+ // Var(I_Re) ≈ Var(I) * cos^2(pi/4) + Var(θ) * I^2 * sin^2(pi/4)
95
+ CHECK (sym_sensor_param.measurement .real_component .variance ==
96
+ doctest::Approx (0.5 * (i_variance_pu + i_angle_variance_pu * i_pu * i_pu)));
97
+ // Var(I_Im) ≈ Var(I) * sin^2(pi/4) + Var(θ) * I^2 * cos^2(pi/4)
98
+ CHECK (sym_sensor_param.measurement .imag_component .variance ==
99
+ doctest::Approx (0.5 * (i_variance_pu + i_angle_variance_pu * i_pu * i_pu)));
100
+ CHECK (real (sym_sensor_param.measurement .value ()) == doctest::Approx (i_pu * cos (i_angle)));
101
+ CHECK (imag (sym_sensor_param.measurement .value ()) == doctest::Approx (i_pu * sin (i_angle)));
102
+
103
+ CHECK (sym_sensor_output.id == 0 );
104
+ CHECK (sym_sensor_output.energized == 1 );
105
+ if (angle_measurement_type == AngleMeasurementType::global_angle) {
106
+ CHECK (sym_sensor_output.i_residual == doctest::Approx (0.0 ));
107
+ CHECK (sym_sensor_output.i_angle_residual == doctest::Approx (0.0 ));
108
+ } else {
109
+ CHECK (sym_sensor_output_local_residuals.i_residual == doctest::Approx (0.0 ));
110
+ CHECK (sym_sensor_output_local_residuals.i_angle_residual == doctest::Approx (0.0 ));
111
+ }
112
+
113
+ // Check symmetric sensor output for asymmetric parameters
114
+ CHECK (asym_sensor_param.measurement .real_component .variance [0 ] ==
115
+ doctest::Approx (0.5 * (i_variance_pu + i_angle_variance_pu * i_pu * i_pu)));
116
+ auto const shifted_i_angle = i_angle + deg_240;
117
+ CHECK (asym_sensor_param.measurement .imag_component .variance [1 ] ==
118
+ doctest::Approx (i_variance_pu * sin (shifted_i_angle) * sin (shifted_i_angle) +
119
+ i_angle_variance_pu * i_pu * i_pu * cos (shifted_i_angle) * cos (shifted_i_angle)));
120
+ CHECK (real (asym_sensor_param.measurement .value ()[0 ]) == doctest::Approx (i_pu * cos (i_angle)));
121
+ CHECK (imag (asym_sensor_param.measurement .value ()[1 ]) == doctest::Approx (i_pu * sin (shifted_i_angle)));
122
+
123
+ CHECK (sym_sensor_output_asym_param.id == 0 );
124
+ CHECK (sym_sensor_output_asym_param.energized == 1 );
125
+ for (auto phase = 0 ; phase < 3 ; ++phase) {
126
+ CAPTURE (phase);
127
+ if (angle_measurement_type == AngleMeasurementType::global_angle) {
128
+ CHECK (sym_sensor_output_asym_param.i_residual [phase] == doctest::Approx (0.0 ));
129
+ CHECK (sym_sensor_output_asym_param.i_angle_residual [phase] == doctest::Approx (0.0 ));
130
+ } else {
131
+ CHECK (sym_sensor_output_asym_param_local_residuals.i_residual [phase] == doctest::Approx (0.0 ));
132
+ CHECK (sym_sensor_output_asym_param_local_residuals.i_angle_residual [phase] ==
133
+ doctest::Approx (0.0 ));
134
+ }
135
+ }
136
+
137
+ CHECK (sym_current_sensor.get_terminal_type () == terminal_type);
138
+ if (angle_measurement_type == AngleMeasurementType::global_angle) {
139
+ CHECK (sym_current_sensor.get_angle_measurement_type () == AngleMeasurementType::global_angle);
140
+ } else {
141
+ CHECK (sym_current_sensor.get_angle_measurement_type () == AngleMeasurementType::local_angle);
142
+ }
82
143
}
83
- // Var(I_Re) ≈ Var(I) * cos^2(pi/4) + Var(θ) * I^2 * sin^2(pi/4)
84
- CHECK (sym_sensor_param.measurement .real_component .variance ==
85
- doctest::Approx (0.5 * (i_variance_pu + i_angle_variance_pu * i_pu * i_pu)));
86
- // Var(I_Im) ≈ Var(I) * sin^2(pi/4) + Var(θ) * I^2 * cos^2(pi/4)
87
- CHECK (sym_sensor_param.measurement .imag_component .variance ==
88
- doctest::Approx (0.5 * (i_variance_pu + i_angle_variance_pu * i_pu * i_pu)));
89
- CHECK (real (sym_sensor_param.measurement .value ()) == doctest::Approx (i_pu * cos (i_angle)));
90
- CHECK (imag (sym_sensor_param.measurement .value ()) == doctest::Approx (i_pu * sin (i_angle)));
91
-
92
- CHECK (sym_sensor_output.id == 0 );
93
- CHECK (sym_sensor_output.energized == 1 );
94
- CHECK (sym_sensor_output.i_residual == doctest::Approx (0.0 ));
95
- CHECK (sym_sensor_output.i_angle_residual == doctest::Approx (0.0 ));
96
-
97
- // Check symmetric sensor output for asymmetric parameters
98
- CHECK (asym_sensor_param.measurement .real_component .variance [0 ] ==
99
- doctest::Approx (0.5 * (i_variance_pu + i_angle_variance_pu * i_pu * i_pu)));
100
- auto const shifted_i_angle = i_angle + deg_240;
101
- CHECK (asym_sensor_param.measurement .imag_component .variance [1 ] ==
102
- doctest::Approx (i_variance_pu * sin (shifted_i_angle) * sin (shifted_i_angle) +
103
- i_angle_variance_pu * i_pu * i_pu * cos (shifted_i_angle) * cos (shifted_i_angle)));
104
- CHECK (real (asym_sensor_param.measurement .value ()[0 ]) == doctest::Approx (i_pu * cos (i_angle)));
105
- CHECK (imag (asym_sensor_param.measurement .value ()[1 ]) == doctest::Approx (i_pu * sin (shifted_i_angle)));
106
-
107
- CHECK (sym_sensor_output_asym_param.id == 0 );
108
- CHECK (sym_sensor_output_asym_param.energized == 1 );
109
- for (auto i = 0 ; i < 3 ; ++i) {
110
- CHECK (sym_sensor_output_asym_param.i_residual [i] == doctest::Approx (0.0 ));
111
- CHECK (sym_sensor_output_asym_param.i_angle_residual [i] == doctest::Approx (0.0 ));
112
- }
113
-
114
- CHECK (sym_current_sensor.get_terminal_type () == terminal_type);
115
-
116
- CHECK (sym_current_sensor.get_angle_measurement_type () == AngleMeasurementType::global_angle);
117
144
}
118
145
SUBCASE (" Wrong measured terminal type" ) {
119
146
for (auto const terminal_type :
0 commit comments