2 Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 #include "certificate_chain.h"
36 #include "compose.hpp"
38 #include "exceptions.h"
39 #include "language_tag.h"
41 #include "reel_mono_picture_asset.h"
42 #include "reel_smpte_subtitle_asset.h"
43 #include "reel_sound_asset.h"
44 #include "stream_operators.h"
47 #include <boost/test/unit_test.hpp>
51 using std::make_shared;
52 using std::shared_ptr;
57 BOOST_AUTO_TEST_CASE (cpl_metadata_bad_values_test)
59 dcp::CPL cpl("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
60 BOOST_CHECK_THROW (cpl.set_version_number(-1), dcp::BadSettingError);
62 vector<dcp::ContentVersion> cv = {
63 dcp::ContentVersion("same-id", "version 1"),
64 dcp::ContentVersion("same-id", "version 2")
66 BOOST_CHECK_THROW (cpl.set_content_versions(cv), dcp::DuplicateIdError);
70 BOOST_AUTO_TEST_CASE (main_sound_configuration_test1)
72 dcp::MainSoundConfiguration msc("51/L,R,C,LFE,-,-");
73 BOOST_CHECK_EQUAL (msc.to_string(), "51/L,R,C,LFE,-,-");
74 BOOST_CHECK_EQUAL (msc.channels(), 6);
75 BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::FIVE_POINT_ONE);
76 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
77 BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
78 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
79 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
80 BOOST_CHECK (!msc.mapping(4));
81 BOOST_CHECK (!msc.mapping(5));
85 BOOST_AUTO_TEST_CASE (main_sound_configuration_test2)
87 dcp::MainSoundConfiguration msc("71/L,R,C,LFE,-,-");
88 BOOST_CHECK_EQUAL (msc.to_string(), "71/L,R,C,LFE,-,-");
89 BOOST_CHECK_EQUAL (msc.channels(), 6);
90 BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
91 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
92 BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
93 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
94 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
95 BOOST_CHECK (!msc.mapping(4));
96 BOOST_CHECK (!msc.mapping(5));
100 BOOST_AUTO_TEST_CASE (main_sound_configuration_test3)
102 dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss");
103 BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss");
104 BOOST_CHECK_EQUAL (msc.channels(), 6);
105 BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
106 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
107 BOOST_CHECK (!msc.mapping(1));
108 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
109 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
110 BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::Channel::LS);
111 BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::Channel::RS);
115 BOOST_AUTO_TEST_CASE (main_sound_configuration_test4)
117 dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
118 BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
119 BOOST_CHECK_EQUAL (msc.channels(), 15);
120 BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
121 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
122 BOOST_CHECK (!msc.mapping(1));
123 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
124 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
125 BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::Channel::LS);
126 BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::Channel::RS);
127 for (int i = 6; i < 15; ++i) {
128 BOOST_CHECK (!msc.mapping(i));
133 BOOST_AUTO_TEST_CASE (main_sound_configuration_test5)
135 dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
136 BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
137 BOOST_CHECK_EQUAL (msc.channels(), 15);
138 BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
139 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
140 BOOST_CHECK (!msc.mapping(1));
141 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
142 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
143 BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::Channel::LS);
144 BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::Channel::RS);
145 BOOST_CHECK_EQUAL (msc.mapping(6).get(), dcp::Channel::HI);
146 BOOST_CHECK_EQUAL (msc.mapping(7).get(), dcp::Channel::VI);
147 BOOST_CHECK (!msc.mapping(8));
148 BOOST_CHECK (!msc.mapping(9));
149 BOOST_CHECK_EQUAL (msc.mapping(10).get(), dcp::Channel::BSL);
150 BOOST_CHECK_EQUAL (msc.mapping(11).get(), dcp::Channel::BSR);
151 BOOST_CHECK_EQUAL (msc.mapping(12).get(), dcp::Channel::MOTION_DATA);
152 BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::Channel::SYNC_SIGNAL);
153 BOOST_CHECK_EQUAL (msc.mapping(14).get(), dcp::Channel::SIGN_LANGUAGE);
157 /* 482-12 says that implementations may use case-insensitive comparisons for the channel identifiers,
158 * and there is one DCP in the private test suite (made by Disney) that uses LS for left surround.
160 BOOST_AUTO_TEST_CASE(main_sound_configuration_test_case_insensitive)
162 dcp::MainSoundConfiguration msc("51/L,-,C,LFE,LS,RS,HI,VIN");
163 BOOST_CHECK_EQUAL(msc.to_string(), "51/L,-,C,LFE,Ls,Rs,HI,VIN");
164 BOOST_CHECK_EQUAL(msc.channels(), 8);
165 BOOST_CHECK_EQUAL(msc.field(), dcp::MCASoundField::FIVE_POINT_ONE);
166 BOOST_CHECK_EQUAL(msc.mapping(0).get(), dcp::Channel::LEFT);
167 BOOST_CHECK(!msc.mapping(1));
168 BOOST_CHECK_EQUAL(msc.mapping(2).get(), dcp::Channel::CENTRE);
169 BOOST_CHECK_EQUAL(msc.mapping(3).get(), dcp::Channel::LFE);
170 BOOST_CHECK_EQUAL(msc.mapping(4).get(), dcp::Channel::LS);
171 BOOST_CHECK_EQUAL(msc.mapping(5).get(), dcp::Channel::RS);
172 BOOST_CHECK_EQUAL(msc.mapping(6).get(), dcp::Channel::HI);
173 BOOST_CHECK_EQUAL(msc.mapping(7).get(), dcp::Channel::VI);
177 BOOST_AUTO_TEST_CASE (luminance_test1)
179 BOOST_CHECK_NO_THROW (dcp::Luminance(4, dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE));
180 BOOST_CHECK_THROW (dcp::Luminance(-4, dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE), dcp::MiscError);
184 BOOST_AUTO_TEST_CASE (luminance_test2)
186 auto doc = make_shared<cxml::Document>("Luminance");
189 "<Luminance units=\"candela-per-square-metre\">4.5</Luminance>"
192 dcp::Luminance lum (doc);
193 BOOST_CHECK (lum.unit() == dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE);
194 BOOST_CHECK_CLOSE (lum.value(), 4.5, 0.1);
198 BOOST_AUTO_TEST_CASE (luminance_test3)
200 auto doc = make_shared<cxml::Document>("Luminance");
203 "<Luminance units=\"candela-per-square-motre\">4.5</Luminance>"
206 BOOST_CHECK_THROW (new dcp::Luminance(doc), dcp::XMLError);
210 BOOST_AUTO_TEST_CASE (luminance_test4)
212 auto doc = make_shared<cxml::Document>("Luminance");
215 "<Luminance units=\"candela-per-square-metre\">-4.5</Luminance>"
218 /* We tolerate out-of-range values when reading from XML */
219 dcp::Luminance lum (doc);
220 BOOST_CHECK (lum.unit() == dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE);
221 BOOST_CHECK_CLOSE (lum.value(), -4.5, 0.1);
225 /** A test where most CPL metadata is present */
226 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test1)
228 dcp::CPL cpl("test/ref/cpl_metadata_test1.xml");
230 BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "full-content-title");
231 BOOST_CHECK (cpl.full_content_title_text_language().get() == "de");
232 BOOST_CHECK (cpl.release_territory().get() == "ES");
233 BOOST_CHECK_EQUAL (cpl.version_number().get(), 2);
234 BOOST_CHECK_EQUAL (cpl.status().get(), dcp::Status::FINAL);
235 BOOST_CHECK_EQUAL (cpl.chain().get(), "the-chain");
236 BOOST_CHECK_EQUAL (cpl.distributor().get(), "the-distributor");
237 BOOST_CHECK_EQUAL (cpl.facility().get(), "the-facility");
238 BOOST_CHECK (cpl.luminance() == dcp::Luminance(4.5, dcp::Luminance::Unit::FOOT_LAMBERT));
240 dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
241 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
242 BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
243 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
244 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
245 BOOST_CHECK (!msc.mapping(4));
246 BOOST_CHECK (!msc.mapping(5));
247 BOOST_CHECK (!msc.mapping(6));
248 BOOST_CHECK (!msc.mapping(7));
249 BOOST_CHECK (!msc.mapping(8));
250 BOOST_CHECK (!msc.mapping(9));
251 BOOST_CHECK (!msc.mapping(10));
252 BOOST_CHECK (!msc.mapping(11));
253 BOOST_CHECK (!msc.mapping(12));
254 BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::Channel::SYNC_SIGNAL);
256 BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
257 BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
258 BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
260 auto reels = cpl.reels ();
261 BOOST_REQUIRE_EQUAL (reels.size(), 1U);
262 BOOST_REQUIRE (reels.front()->main_subtitle()->language());
263 BOOST_CHECK_EQUAL (reels.front()->main_subtitle()->language().get(), "de-DE");
265 auto asl = cpl.additional_subtitle_languages();
266 BOOST_REQUIRE_EQUAL (asl.size(), 2U);
267 BOOST_CHECK_EQUAL (asl[0], "en-US");
268 BOOST_CHECK_EQUAL (asl[1], "fr-ZA");
270 BOOST_CHECK (cpl.additional_subtitle_languages() == asl);
274 /** A test where most CPL metadata is present */
275 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
279 dcp::CPL cpl("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
280 cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
282 vector<dcp::ContentVersion> cv = {
283 dcp::ContentVersion("some-id", "version 1"),
284 dcp::ContentVersion("another-id", "version 2")
286 cpl.set_content_versions (cv);
288 cpl.set_full_content_title_text ("full-content-title");
289 cpl.set_full_content_title_text_language (dcp::LanguageTag("de"));
290 cpl.set_release_territory (dcp::LanguageTag::RegionSubtag("ES"));
291 cpl.set_version_number (2);
292 cpl.set_status (dcp::Status::FINAL);
293 cpl.set_chain ("the-chain");
294 cpl.set_distributor ("the-distributor");
295 cpl.set_facility ("the-facility");
296 cpl.set_luminance (dcp::Luminance(4.5, dcp::Luminance::Unit::FOOT_LAMBERT));
297 cpl.set_issuer ("libdcp1.6.4devel");
298 cpl.set_creator ("libdcp1.6.4devel");
300 dcp::MainSoundConfiguration msc(dcp::MCASoundField::SEVEN_POINT_ONE, 16);
301 msc.set_mapping (0, dcp::Channel::LEFT);
302 msc.set_mapping (1, dcp::Channel::RIGHT);
303 msc.set_mapping (2, dcp::Channel::CENTRE);
304 msc.set_mapping (3, dcp::Channel::LFE);
305 msc.set_mapping (13, dcp::Channel::SYNC_SIGNAL);
306 cpl.set_main_sound_configuration(msc);
308 cpl.set_main_sound_sample_rate (48000);
309 cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
310 cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
312 auto doc = make_shared<cxml::Document>("MainSubtitle");
316 "<Id>urn:uuid:8bca1489-aab1-9259-a4fd-8150abc1de12</Id>"
317 "<AnnotationText>Goodbye world!</AnnotationText>"
318 "<EditRate>25 1</EditRate>"
319 "<IntrinsicDuration>1870</IntrinsicDuration>"
320 "<EntryPoint>0</EntryPoint>"
321 "<Duration>525</Duration>"
322 "<KeyId>urn:uuid:540cbf10-ab14-0233-ab1f-fb31501cabfa</KeyId>"
323 "<Hash>3EABjX9BB1CAWhLUtHhrGSyLgOY=</Hash>"
324 "<Language>de-DE</Language>"
328 auto reel = make_shared<dcp::Reel>();
329 reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
330 reel->add (make_shared<dcp::ReelSMPTESubtitleAsset>(doc));
333 auto lt = { dcp::LanguageTag("en-US"), dcp::LanguageTag("fr-ZA") };
334 cpl.set_additional_subtitle_languages (lt);
336 cpl.set_sign_language_video_language (dcp::LanguageTag("bzs"));
338 cpl.write_xml ("build/test/cpl_metadata_write_test1.xml", {});
340 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
341 dcp::file_to_string("build/test/cpl_metadata_write_test1.xml"),
347 /** A test where most CPL metadata is present */
348 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_1)
350 dcp::CPL cpl ("test/ref/cpl_metadata_test1.xml");
351 cpl.write_xml ("build/test/cpl_metadata_roundtrip_test1.xml", shared_ptr<dcp::CertificateChain>());
353 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
354 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test1.xml"),
360 /** A test where only a bare minimum of CPL metadata is present */
361 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test2)
365 dcp::CPL cpl("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
366 cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
367 cpl.set_content_version (dcp::ContentVersion("id", "version"));
368 cpl.set_issuer ("libdcp1.6.4devel");
369 cpl.set_creator ("libdcp1.6.4devel");
371 dcp::MainSoundConfiguration msc(dcp::MCASoundField::SEVEN_POINT_ONE, 16);
372 msc.set_mapping (0, dcp::Channel::LEFT);
373 msc.set_mapping (1, dcp::Channel::RIGHT);
374 msc.set_mapping (2, dcp::Channel::CENTRE);
375 msc.set_mapping (3, dcp::Channel::LFE);
376 msc.set_mapping (13, dcp::Channel::SYNC_SIGNAL);
377 cpl.set_main_sound_configuration(msc);
379 cpl.set_main_sound_sample_rate (48000);
380 cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
381 cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
383 auto reel = make_shared<dcp::Reel>();
384 reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
387 cpl.write_xml ("build/test/cpl_metadata_write_test2.xml", {});
389 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
390 dcp::file_to_string("build/test/cpl_metadata_write_test2.xml"),
396 /** A test where only a bare minimum of CPL metadata is present */
397 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
399 dcp::CPL cpl("test/ref/cpl_metadata_test2.xml");
401 BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "");
402 BOOST_CHECK (!cpl.full_content_title_text_language());
403 BOOST_CHECK (!cpl.release_territory());
404 BOOST_CHECK (!cpl.version_number());
405 BOOST_CHECK (!cpl.status());
406 BOOST_CHECK (!cpl.chain());
407 BOOST_CHECK (!cpl.distributor());
408 BOOST_CHECK (!cpl.facility());
409 BOOST_CHECK (!cpl.luminance());
411 dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
412 BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
413 BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
414 BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
415 BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
416 BOOST_CHECK (!msc.mapping(4));
417 BOOST_CHECK (!msc.mapping(5));
418 BOOST_CHECK (!msc.mapping(6));
419 BOOST_CHECK (!msc.mapping(7));
420 BOOST_CHECK (!msc.mapping(8));
421 BOOST_CHECK (!msc.mapping(9));
422 BOOST_CHECK (!msc.mapping(10));
423 BOOST_CHECK (!msc.mapping(11));
424 BOOST_CHECK (!msc.mapping(12));
425 BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::Channel::SYNC_SIGNAL);
427 BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
428 BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
429 BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
431 auto reels = cpl.reels ();
432 BOOST_REQUIRE_EQUAL (reels.size(), 1U);
436 /** A test where only a bare minimum of CPL metadata is present */
437 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_2)
439 dcp::CPL cpl ("test/ref/cpl_metadata_test2.xml");
440 cpl.write_xml ("build/test/cpl_metadata_roundtrip_test2.xml", shared_ptr<dcp::CertificateChain>());
442 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
443 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test2.xml"),
449 BOOST_AUTO_TEST_CASE(check_that_missing_full_content_title_text_is_tolerated)
451 dcp::CPL cpl("test/ref/cpl_metadata_test3.xml");
455 BOOST_AUTO_TEST_CASE(check_sign_language_video_language)
457 dcp::CPL cpl("test/ref/cpl_metadata_test3.xml");
458 cpl.set_sign_language_video_language(dcp::LanguageTag("es-PT"));
459 cpl.write_xml("build/test/check_sign_language_video_language.xml", {});
461 dcp::file_to_string("test/ref/cpl_metadata_test4.xml"),
462 dcp::file_to_string("build/test/check_sign_language_video_language.xml"),
466 dcp::CPL check("build/test/check_sign_language_video_language.xml");
467 BOOST_CHECK_EQUAL(check.sign_language_video_language().get_value_or(""), "es-PT");
472 BOOST_AUTO_TEST_CASE(check_dolby_edr_metadata)
474 dcp::CPL cpl("test/ref/cpl_metadata_test3.xml");
475 cpl.set_dolby_edr_image_transfer_function("PQ10K");
476 cpl.write_xml("build/test/check_dolby_edr_metadata.xml", {});
478 dcp::file_to_string("test/ref/cpl_metadata_test5.xml"),
479 dcp::file_to_string("build/test/check_dolby_edr_metadata.xml"),
483 dcp::CPL check("build/test/check_dolby_edr_metadata.xml");
484 BOOST_CHECK_EQUAL(check.dolby_edr_image_transfer_function().get_value_or(""), "PQ10K");