Add support for a Frequency Unit

Issue #7 resolved
JL_Perc created an issue

In lieu of creating extensibility (#6) or as well as adding extensibility to the library, please add a basic Frequency hertz unit.

Comments (5)

  1. Bruce Santier repo owner

    Thanks for the interest!

    I had originally not created a frequency unit because it can be represented as the inverse of time (hertz are just 1/s, and time is a base unit Fling supports). I’m not aware of a unit other than hertz that are commonly used for this. I’d be curious to learn:

    • Are you planning on converting to/from something other than hertz?
    • If you are able to say, what sorts of things are you measuring (temporal frequency only, or also angular frequency, spatial frequency…?)
    • What sort of operations would you need to perform with these measurements (e.g. multiplying with other units)? If you can provide a simple example, that would really help.

    The library supports hertz via “Derived Measurements”, here is an example:

    final measurement = DerivedMeasurement.divide(20.units, 1.seconds, "hertz");
    print("\nI can spin this wheel 20 times per second, or $measurement (${measurement.as(units, milli.seconds)} kilohertz)");
    print("That is ${measurement.as(units, hours)} spins per hour or ${measurement.as(units, milli.hours)} kilo-spins per hour");
    

    Unfortunately, I don’t support all the syntactic sugar for derived units (e.g. extensions on num), so they're a little clunkier to work with. That said, if I had an idea of the sorts of things you'd be doing with it, I could create an implementation of a frequency unit that would have these features. Please let me know!

  2. JL_Perc reporter

    We do a lot of things with positioning and radio frequency analysis. So being able to support hertz as a base unit would make our code much cleaner. easy conversion between the different things was want I wanted and to be able to easily convert between various magnitudes of hertz. Not worried about conversion between rpm or anything like that. Just wanted to treat it like the other si units. IE, we may store data base in hertz, but display it in MHz or GHz. So just wanted a clean way for programmatically mapping and manipulating those pieces of data for display.

    So, I created a class that mimics some of the functionality that you have.

    enum FrequencyUnit {
      milliHz._(10e-3),
      centiHz._(10e-2),
      decaHz._(10e-1),
      Hz._(10e0),
      dekaHz._(10e1),
      hectoHz._(10e2),
      kiloHz._(10e3),
      MHz._(10e6),
      GHz._(10e9),
      THz._(10e12);
    
      final double _conversionFactor;
    
      const FrequencyUnit._(this._conversionFactor);
    
      double as(final num value, FrequencyUnit unit) {
        var dblValue = value.toDouble();
        if (unit == this) {
          return dblValue;
        }
        return dblValue * _conversionFactor / unit._conversionFactor;
      }
    }
    
    class Frequency implements Comparable<Frequency> {
      static const zero = Frequency.milliHz(0.0);
      static const infinity = Frequency.milliHz(double.infinity);
      static const nan = Frequency.milliHz(double.nan);
      static const maxFinite = Frequency.milliHz(double.maxFinite);
      static const minPositive = Frequency.milliHz(double.minPositive);
      static const negativeInfinity = Frequency.milliHz(double.negativeInfinity);
    
      final double value;
      final FrequencyUnit unit;
    
      const Frequency._(this.value, this.unit);
    
      const Frequency.milliHz(this.value) : this.unit = FrequencyUnit.milliHz;
    
      const Frequency.centiHz(this.value) : this.unit = FrequencyUnit.centiHz;
    
      const Frequency.deciHz(this.value) : this.unit = FrequencyUnit.decaHz;
    
      const Frequency.Hz(this.value) : this.unit = FrequencyUnit.Hz;
    
      const Frequency.dekaHz(this.value) : this.unit = FrequencyUnit.dekaHz;
    
      const Frequency.hectoHz(this.value) : this.unit = FrequencyUnit.hectoHz;
    
      const Frequency.kiloHz(this.value) : this.unit = FrequencyUnit.kiloHz;
    
      const Frequency.MHz(this.value) : this.unit = FrequencyUnit.MHz;
    
      const Frequency.GHz(this.value) : this.unit = FrequencyUnit.GHz;
    
      const Frequency.THz(this.value) : this.unit = FrequencyUnit.THz;
    
      Frequency as(FrequencyUnit unit) => Frequency._(this.unit.as(value, unit), unit);
        /// Returns `true` if this value is negative.
      bool get isNegative => value.isNegative;
    
      /// Returns `true` if this value is infinite (either positive or negative).
      bool get isInfinite => value.isInfinite;
    
      /// Returns `true` if this value is finite.
      bool get isFinite => value.isFinite;
    
      /// Returns `true` if this value cannot be expressed as a number.
      bool get isNaN => value.isNaN;
    
      double _otherValue(Frequency other) {
        return other.as(this.unit).value;
      }
    
      @override
      bool operator ==(final Object other) => identical(this, other) || (other is Frequency && value == _otherValue(other));
    
      @override
      int get hashCode => this.unit.as(this.value, FrequencyUnit.Hz).hashCode;
    
      @override
      int compareTo(final Frequency other) => this.value.compareTo(_otherValue(other));
    
      //TODO: add math operators
    }
    

  3. Bruce Santier repo owner

    Thanks, I think I understand - you are mostly interested in the prefix conversions. I think my example is a bit overkill in this case. I might even argue that using the whole library is a bit overkill, and your code is probably sufficient.

    That said, I have pushed a basic Frequency unit to version 2.2.7 - you should be able to use it like any other unit, it just doesn’t have any conversions other than the prefix conversions. You can do:

    final frequency = 123.hertz;
    print(frequency);                                      // 123 Hz
    print("${frequency.as(mega.hertz)} ${mega.hertz}");    // 0.00123 MHz
    

    Hopefully that meets your needs.

  4. Log in to comment