2424
2525#define WITH_CMD_DELAY 0 /* TODO See which devices need delays. */
2626
27+ /* OWON XDM range definitions */
28+ static const char * owon_dcv_ranges [] = {
29+ "auto" , "50 mV" , "500 mV" , "5 V" , "50 V" , "500 V" , "1000 V" , NULL
30+ };
31+
32+ static const char * owon_acv_ranges [] = {
33+ "auto" , "500 mV" , "5 V" , "50 V" , "500 V" , "750 V" , NULL
34+ };
35+
36+ static const char * owon_dci_ranges [] = {
37+ "auto" , "500 uA" , "5 mA" , "50 mA" , "500 mA" , "5 A" , "10 A" , NULL
38+ };
39+
40+ static const char * owon_aci_ranges [] = {
41+ "auto" , "500 uA" , "5 mA" , "50 mA" , "500 mA" , "5 A" , "10 A" , NULL
42+ };
43+
44+ static const char * owon_res_ranges [] = {
45+ "auto" , "500 Ohm" , "5 kOhm" , "50 kOhm" , "500 kOhm" , "5 MOhm" , "50 MOhm" , NULL
46+ };
47+
48+ static const char * owon_cap_ranges [] = {
49+ "auto" , "50 nF" , "500 nF" , "5 uF" , "50 uF" , "500 uF" , "5 mF" , "50 mF" , NULL
50+ };
51+
52+ static const char * owon_temp_ranges [] = {
53+ "KITS90" , "Pt100" , NULL /* no auto here */
54+ };
55+
2756SR_PRIV void scpi_dmm_cmd_delay (struct sr_scpi_dev_inst * scpi )
2857{
2958 if (WITH_CMD_DELAY )
@@ -216,6 +245,99 @@ SR_PRIV const char *scpi_dmm_get_range_text(const struct sr_dev_inst *sdi)
216245 return devc -> range_text ;
217246}
218247
248+ /*
249+ * We use human-readable range texts, including the unit. They are mostly the same
250+ * as displayed on the device, but with some differences:
251+ * - The unit is always separated from the number by a space.
252+ * - The Unicode Omega symbol (0xCE 0xA9) is replaced with "Ohm".
253+ *
254+ * Here is all the possible range answers, that I got from XDM1041 over SCPI:
255+ * DCV: 1000 V␍␊ 500 V␍␊ 50 V␍␊ 5 V␍␊ 500 mV␍␊ 50 mV␍␊
256+ * ACV 750 V␍␊500 V␍␊ 50 V␍␊5 V␍␊500 mV␍␊
257+ * DCI: 10 A␍␊5 A␍␊ 500 mA␍␊50 mA␍␊5 mA␍␊500 uA␍␊
258+ * ACI: 10 A␍␊5 A␍␊500 mA␍␊50 mA␍␊5 mA␍␊500 uA␍␊
259+ * RES: 50 M<0xce><0xa9>␍␊5 M<0xce><0xa9>␍␊500 K<0xce><0xa9>␍␊50 K<0xce><0xa9>␍␊5 K<0xce><0xa9>␍␊500 <0xce><0xa9>␍␊500 <0xce><0xa9>␍␊500 <0xce><0xa9>␍␊
260+ * CAP: 50 mF␍␊5 mF␍␊500uF␍␊50uF␍␊5uF␍␊500 nF␍␊50 nF␍␊
261+ * Freq: Hz␍␊
262+ * Period: s␍␊
263+ * Temp: KITS90␍␊Pt100␍␊
264+ */
265+ SR_PRIV const char * scpi_dmm_owon_get_range_text (const struct sr_dev_inst * sdi )
266+ {
267+ struct dev_context * devc ;
268+ int ret ;
269+ const struct mqopt_item * mqitem ;
270+ gboolean is_auto ;
271+ char * response , * pos ;
272+
273+ devc = sdi -> priv ;
274+
275+ ret = scpi_dmm_get_mq (sdi , NULL , NULL , NULL , & mqitem );
276+ if (ret != SR_OK )
277+ return NULL ;
278+ if (!mqitem || !mqitem -> scpi_func_setup )
279+ return NULL ;
280+ if (mqitem -> drv_flags & FLAG_NO_RANGE )
281+ return NULL ;
282+
283+ scpi_dmm_cmd_delay (sdi -> conn );
284+ ret = sr_scpi_cmd (sdi , devc -> cmdset , 0 , NULL ,
285+ DMM_CMD_QUERY_RANGE_AUTO , mqitem -> scpi_func_setup );
286+ if (ret != SR_OK )
287+ return NULL ;
288+ ret = sr_scpi_get_bool (sdi -> conn , NULL , & is_auto );
289+ if (ret != SR_OK )
290+ return NULL ;
291+ if (is_auto )
292+ return "auto" ;
293+
294+ scpi_dmm_cmd_delay (sdi -> conn );
295+ ret = sr_scpi_cmd (sdi , devc -> cmdset , 0 , NULL ,
296+ DMM_CMD_QUERY_RANGE , mqitem -> scpi_func_setup );
297+ if (ret != SR_OK )
298+ return NULL ;
299+ response = NULL ;
300+ ret = sr_scpi_get_string (sdi -> conn , NULL , & response );
301+ if (ret != SR_OK ) {
302+ g_free (response );
303+ return NULL ;
304+ }
305+ /* Replace Unicode Omega symbol (0xCE 0xA9) with "Ohm". */
306+ GString * response_str = g_string_new (response );
307+ g_string_replace (response_str , "\xCE\xA9" , "Ohm" , 0 );
308+ g_free (response );
309+ response = g_string_free (response_str , FALSE);
310+
311+ /* Check if space is needed between number and units. */
312+ pos = response ;
313+
314+ /* Skip leading whitespace. */
315+ while (* pos && g_ascii_isspace (* pos ))
316+ pos ++ ;
317+
318+ /* Find where the number ends. */
319+ char * number_end = pos ;
320+ while (* number_end && (g_ascii_isdigit (* number_end ) || * number_end == '.' ||
321+ * number_end == '+' || * number_end == '-' || * number_end == 'e' ||
322+ * number_end == 'E' ))
323+ number_end ++ ;
324+
325+ /* Check if we found a number and there's a unit character immediately after (no space). */
326+ if (number_end > pos && * number_end && !g_ascii_isspace (* number_end )) {
327+ /* Need to insert space between number and units. */
328+ size_t number_len = number_end - pos ;
329+ snprintf (devc -> range_text , sizeof (devc -> range_text ), "%.*s %s" ,
330+ (int )number_len , pos , number_end );
331+ g_free (response );
332+ return devc -> range_text ;
333+ } else {
334+ /* Response is fine as is, just copy it. */
335+ snprintf (devc -> range_text , sizeof (devc -> range_text ), "%s" , response );
336+ g_free (response );
337+ return devc -> range_text ;
338+ }
339+ }
340+
219341SR_PRIV int scpi_dmm_set_range_from_text (const struct sr_dev_inst * sdi ,
220342 const char * range )
221343{
@@ -249,6 +371,107 @@ SR_PRIV int scpi_dmm_set_range_from_text(const struct sr_dev_inst *sdi,
249371 return SR_OK ;
250372}
251373
374+ /* OWON XDM DMMs have two different methods to set range:
375+ * "CONF:VOLT 0.05" will set the range to 50 mV (note the absence of units)
376+ * "RANGE 5" will set the range to fifth option in a list of possible ranges for current measurement mode
377+ * Although the second one would be easier to implement, the first one should not be affected by future changes in firmware
378+ */
379+ SR_PRIV int scpi_dmm_owon_set_range_from_text (const struct sr_dev_inst * sdi ,
380+ const char * range )
381+ {
382+ struct dev_context * devc ;
383+ int ret ;
384+ const struct mqopt_item * item ;
385+ gboolean is_auto ;
386+ char processed_range [64 ];
387+
388+ devc = sdi -> priv ;
389+
390+ if (!range || !* range )
391+ return SR_ERR_ARG ;
392+
393+ ret = scpi_dmm_get_mq (sdi , NULL , NULL , NULL , & item );
394+ if (ret != SR_OK )
395+ return ret ;
396+ if (!item || !item -> scpi_func_setup )
397+ return SR_ERR_ARG ;
398+ if (item -> drv_flags & FLAG_NO_RANGE )
399+ return SR_ERR_NA ;
400+
401+ is_auto = g_ascii_strcasecmp (range , "auto" ) == 0 ;
402+
403+ /* Preprocess range text to handle SI prefixes */
404+ const char * space_pos = strchr (range , ' ' );
405+ if (space_pos && * (space_pos + 1 )) {
406+ /* Extract the numeric part */
407+ size_t num_len = space_pos - range ;
408+ char num_str [32 ];
409+ strncpy (num_str , range , num_len );
410+ num_str [num_len ] = '\0' ;
411+
412+ /* Parse the numeric value */
413+ double value ;
414+ ret = sr_atod_ascii (num_str , & value );
415+ if (ret == SR_OK ) {
416+ /* Check for SI prefix after the space */
417+ char prefix = * (space_pos + 1 );
418+ double multiplier = 1.0 ;
419+
420+ switch (prefix ) {
421+ case 'k' :
422+ case 'K' :
423+ multiplier = 1e3 ;
424+ break ;
425+ case 'm' :
426+ multiplier = 1e-3 ;
427+ break ;
428+ case 'u' :
429+ multiplier = 1e-6 ;
430+ break ;
431+ case 'n' :
432+ multiplier = 1e-9 ;
433+ break ;
434+ case 'p' :
435+ multiplier = 1e-12 ;
436+ break ;
437+ case 'M' :
438+ multiplier = 1e6 ;
439+ break ;
440+ case 'G' :
441+ multiplier = 1e9 ;
442+ break ;
443+ default :
444+ /* No recognized SI prefix, use original range */
445+ snprintf (processed_range , sizeof (processed_range ), "%s" , range );
446+ multiplier = 0.0 ; /* Flag to use original range */
447+ break ;
448+ }
449+
450+ if (multiplier != 0.0 ) {
451+ /* Apply the multiplier and format the result with locale-independent formatting */
452+ value *= multiplier ;
453+ g_ascii_dtostr (processed_range , sizeof (processed_range ), value );
454+ }
455+ } else {
456+ /* Failed to parse number, use original range */
457+ snprintf (processed_range , sizeof (processed_range ), "%s" , range );
458+ }
459+ } else {
460+ /* No space found, use original range */
461+ snprintf (processed_range , sizeof (processed_range ), "%s" , range );
462+ }
463+
464+ scpi_dmm_cmd_delay (sdi -> conn );
465+ ret = sr_scpi_cmd (sdi , devc -> cmdset , 0 , NULL , DMM_CMD_SETUP_RANGE ,
466+ item -> scpi_func_setup , is_auto ? "AUTO" : processed_range );
467+ if (ret != SR_OK )
468+ return ret ;
469+ if (item -> drv_flags & FLAG_CONF_DELAY )
470+ g_usleep (devc -> model -> conf_delay_us );
471+
472+ return SR_OK ;
473+ }
474+
252475SR_PRIV GVariant * scpi_dmm_get_range_text_list (const struct sr_dev_inst * sdi )
253476{
254477 GVariantBuilder gvb ;
@@ -269,6 +492,75 @@ SR_PRIV GVariant *scpi_dmm_get_range_text_list(const struct sr_dev_inst *sdi)
269492 return list ;
270493}
271494
495+ SR_PRIV GVariant * scpi_dmm_owon_get_range_text_list (const struct sr_dev_inst * sdi )
496+ {
497+ GVariantBuilder gvb ;
498+ GVariant * list ;
499+ int ret ;
500+ enum sr_mq mq ;
501+ enum sr_mqflag mqflag ;
502+ const char * * ranges = NULL ;
503+ const struct mqopt_item * mqitem ;
504+ int i ;
505+
506+ /* Explicitly use string array type, otherwise empty array won't be typed */
507+ g_variant_builder_init (& gvb , G_VARIANT_TYPE_STRING_ARRAY );
508+
509+ /* Get current measurement quantity to return appropriate ranges */
510+ ret = scpi_dmm_get_mq (sdi , & mq , & mqflag , NULL , & mqitem );
511+ if (ret != SR_OK ) {
512+ /* Return empty list if we can't determine current mode */
513+ list = g_variant_builder_end (& gvb );
514+ return list ;
515+ }
516+
517+ /* Check if current mode has no range support */
518+ if (mqitem && (mqitem -> drv_flags & FLAG_NO_RANGE )) {
519+ /* Return empty list for modes that don't support ranges */
520+ list = g_variant_builder_end (& gvb );
521+ return list ;
522+ }
523+ /* Select appropriate range array based on current measurement type */
524+ switch (mq ) {
525+ case SR_MQ_VOLTAGE :
526+ if (mqflag & SR_MQFLAG_DC ) {
527+ ranges = owon_dcv_ranges ;
528+ } else if (mqflag & SR_MQFLAG_AC ) {
529+ ranges = owon_acv_ranges ;
530+ }
531+ break ;
532+ case SR_MQ_CURRENT :
533+ if (mqflag & SR_MQFLAG_DC ) {
534+ ranges = owon_dci_ranges ;
535+ } else if (mqflag & SR_MQFLAG_AC ) {
536+ ranges = owon_aci_ranges ;
537+ }
538+ break ;
539+ case SR_MQ_RESISTANCE :
540+ ranges = owon_res_ranges ;
541+ break ;
542+ case SR_MQ_CAPACITANCE :
543+ ranges = owon_cap_ranges ;
544+ break ;
545+ case SR_MQ_TEMPERATURE :
546+ ranges = owon_temp_ranges ;
547+ break ;
548+ default :
549+ /* For other modes, just provide auto */
550+ break ;
551+ }
552+
553+ /* Add all ranges from the selected array */
554+ if (ranges ) {
555+ for (i = 0 ; ranges [i ] != NULL ; i ++ ) {
556+ g_variant_builder_add (& gvb , "s" , ranges [i ]);
557+ }
558+ }
559+
560+ list = g_variant_builder_end (& gvb );
561+ return list ;
562+ }
563+
272564SR_PRIV int scpi_dmm_get_meas_agilent (const struct sr_dev_inst * sdi , size_t ch )
273565{
274566 struct sr_scpi_dev_inst * scpi ;
0 commit comments