Class: Monotonic::Measurement
- Inherits:
-
Object
- Object
- Monotonic::Measurement
- Includes:
- Comparable
- Defined in:
- lib/Monotonic/Measurement.rb
Overview
An elapsed interval together with what it is worth. The count alone says nothing of how much of itself is real, so #to_s shows only as many digits as the uncertainty supports. Nothing is capped: a day resolved to a hundred nanoseconds prints every figure it has, which looks absurd and is — the absurdity being the mismatch between what the instrument resolves and what was asked of it, and better seen than rounded away.
The doubt is a Measurand's business and the unit is a Duration's; what is left here is the ladder between them and the string at the end of it.
Constant Summary collapse
- UNITS =
Powers of a thousand, and no further. The scheme is a decimal-places one, so minutes and hours have no place in it: 90s is plainer than 1.5min, and a run of a day reads as 86400s rather than reaching for a unit which does not divide by ten.
[[1, 'ns'], [1_000, 'us'], [1_000_000, 'ms'], [1_000_000_000, 's']]
Instance Attribute Summary collapse
-
#drift ⇒ Object
readonly
Returns the value of attribute drift.
-
#nanoseconds ⇒ Object
readonly
class << self.
Class Method Summary collapse
-
.from(measurand) ⇒ Object
Built from a measurand rather than from a floor, for what comes back from arithmetic: its uncertainty has been propagated rather than measured, so it is neither an instrument's floor nor has a drift left to apply a second time.
Instance Method Summary collapse
-
#*(multiplier) ⇒ Object
Scaled by a number it stays an interval, the uncertainty scaling with it.
-
#+(addend) ⇒ Object
The arithmetic is Measurand's throughout — this only decides what may be combined with what, and hands back the right kind of thing.
- #-(subtrahend) ⇒ Object
-
#/(divisor) ⇒ Object
Divided by a number it stays an interval.
-
#<=>(other) ⇒ Object
Against another measurement only.
-
#decimals ⇒ Object
The least significant digit worth showing is the one the uncertainty reaches, which Measurand settles by the Particle Data Group convention and reports as #place, a power of ten.
-
#floor ⇒ Object
The floor may be given as a number or as anything which answers to #call, so that measuring it can wait until somebody asks about doubt.
- #inspect ⇒ Object
-
#measurand ⇒ Object
The floor is absolute and tells upon short intervals; drift is relative and tells upon long ones, being the rate at which the clock's own oscillator wanders.
- #scale ⇒ Object
- #to_duration ⇒ Object
- #to_f ⇒ Object
-
#to_i ⇒ Object
Both answer in nanoseconds, this measurement's own unit, as Duration::Minutes answers 5 rather than 300.
- #to_s ⇒ Object
- #uncertainty ⇒ Object
- #unit ⇒ Object
- #unit_name ⇒ Object
Instance Attribute Details
#drift ⇒ Object (readonly)
Returns the value of attribute drift.
37 38 39 |
# File 'lib/Monotonic/Measurement.rb', line 37 def drift @drift end |
#nanoseconds ⇒ Object (readonly)
class << self
36 37 38 |
# File 'lib/Monotonic/Measurement.rb', line 36 def nanoseconds @nanoseconds end |
Class Method Details
.from(measurand) ⇒ Object
Built from a measurand rather than from a floor, for what comes back from arithmetic: its uncertainty has been propagated rather than measured, so it is neither an instrument's floor nor has a drift left to apply a second time.
31 32 33 |
# File 'lib/Monotonic/Measurement.rb', line 31 def from(measurand) allocate.tap{|measurement| measurement.send(:build, measurand)} end |
Instance Method Details
#*(multiplier) ⇒ Object
Scaled by a number it stays an interval, the uncertainty scaling with it. Multiplied by another interval it would be time squared, which has no unit here, so it is refused as Duration::Common refuses it.
78 79 80 81 82 83 |
# File 'lib/Monotonic/Measurement.rb', line 78 def *(multiplier) if interval?(multiplier) raise TypeError, "can't multiply #{self.class} by #{multiplier.class}: there is no unit of time squared" end self.class.from(measurand * multiplier) end |
#+(addend) ⇒ Object
The arithmetic is Measurand's throughout — this only decides what may be combined with what, and hands back the right kind of thing. Two intervals add and subtract to an interval, their uncertainties going in quadrature.
67 68 69 |
# File 'lib/Monotonic/Measurement.rb', line 67 def +(addend) self.class.from(measurand + as_measurand(addend, :add)) end |
#-(subtrahend) ⇒ Object
71 72 73 |
# File 'lib/Monotonic/Measurement.rb', line 71 def -(subtrahend) self.class.from(measurand - as_measurand(subtrahend, :subtract)) end |
#/(divisor) ⇒ Object
Divided by a number it stays an interval. Divided by another interval the units cancel and a Measurand is left — dimensionless, but still knowing how well it is known, which is what a speedup or a rate is.
88 89 90 91 |
# File 'lib/Monotonic/Measurement.rb', line 88 def /(divisor) return measurand / divisor.measurand if divisor.respond_to?(:measurand) self.class.from(measurand / divisor) end |
#<=>(other) ⇒ Object
Against another measurement only. A bare number has no unit, and comparing against one would have to assume which was meant, so nil is returned and == is false, as Duration::Common does.
140 141 142 143 |
# File 'lib/Monotonic/Measurement.rb', line 140 def <=>(other) return nil unless other.is_a?(Monotonic::Measurement) @nanoseconds <=> other.nanoseconds end |
#decimals ⇒ Object
The least significant digit worth showing is the one the uncertainty reaches, which Measurand settles by the Particle Data Group convention and reports as #place, a power of ten. Rendering in a coarser unit moves it along by however many tens that unit is worth. A measurand with no uncertainty has no last real digit, and answers nil.
110 111 112 113 114 115 |
# File 'lib/Monotonic/Measurement.rb', line 110 def decimals @decimals ||= ( place = measurand.place place ? [Math.log10(scale) - place, 0].max.to_i : Math.log10(scale).to_i ) end |
#floor ⇒ Object
The floor may be given as a number or as anything which answers to #call, so that measuring it can wait until somebody asks about doubt. Monotonic::Timer hands over its .floor method rather than its value, and a caller which only wants a number never sets the thousand null measurements going.
44 45 46 |
# File 'lib/Monotonic/Measurement.rb', line 44 def floor @floor = @floor.respond_to?(:call) ? @floor.call : @floor end |
#inspect ⇒ Object
145 146 147 |
# File 'lib/Monotonic/Measurement.rb', line 145 def inspect "#<#{self.class} #{self} (#{measurand} ns)>" end |
#measurand ⇒ Object
The floor is absolute and tells upon short intervals; drift is relative and tells upon long ones, being the rate at which the clock's own oscillator wanders. Combining them is not addition — they are independent, so they go in quadrature — which is why the arithmetic is Measurand's and not done here.
53 54 55 56 57 58 |
# File 'lib/Monotonic/Measurement.rb', line 53 def measurand @measurand ||= ( absolute = Measurand.new(@nanoseconds, floor) @drift ? absolute * Measurand.relative(1, @drift) : absolute ) end |
#scale ⇒ Object
97 98 99 |
# File 'lib/Monotonic/Measurement.rb', line 97 def scale unit.first end |
#to_duration ⇒ Object
117 118 119 |
# File 'lib/Monotonic/Measurement.rb', line 117 def to_duration Duration::Nanoseconds.new(@nanoseconds) end |
#to_f ⇒ Object
129 130 131 |
# File 'lib/Monotonic/Measurement.rb', line 129 def to_f @nanoseconds.to_f end |
#to_i ⇒ Object
Both answer in nanoseconds, this measurement's own unit, as Duration::Minutes answers 5 rather than 300. Every other unit comes from #to_duration, which names the one it is asked for. A number handed out without its unit stated must at least always mean the same thing.
125 126 127 |
# File 'lib/Monotonic/Measurement.rb', line 125 def to_i @nanoseconds end |
#to_s ⇒ Object
133 134 135 |
# File 'lib/Monotonic/Measurement.rb', line 133 def to_s format("%.#{decimals}f #{unit_name}", @nanoseconds.to_f / scale) end |
#uncertainty ⇒ Object
60 61 62 |
# File 'lib/Monotonic/Measurement.rb', line 60 def uncertainty measurand.uncertainty end |
#unit ⇒ Object
93 94 95 |
# File 'lib/Monotonic/Measurement.rb', line 93 def unit @unit ||= UNITS.reverse.find{|scale, _| @nanoseconds.abs >= scale} || UNITS.first end |
#unit_name ⇒ Object
101 102 103 |
# File 'lib/Monotonic/Measurement.rb', line 101 def unit_name unit.last end |