Class: WSK::Functions::Function

Inherits:
Object
  • Object
show all
Includes:
Common
Defined in:
lib/WSK/Functions.rb

Overview

Class implementing a mathematical function that can then be used in many contexts

Instance Method Summary collapse

Methods included from Common

#accessInputWaveFile, #accessOutputWaveFile, #getWaveFileAccesses, #parsePlugins, #readDuration, #readFFTProfile, #readThresholds, #val2db

Constructor Details

#initializeFunction

Constructor



65
66
67
68
69
70
71
# File 'lib/WSK/Functions.rb', line 65

def initialize
  # The underlying Ruby function
  @Function = nil
  # The C libraries
  @FunctionUtils = nil
  @VolumeUtils = nil
end

Instance Method Details

#apply_damping(iSlopeUp, iSlopeDown) ⇒ Object

Apply damping.

Parameters
  • iSlopeUp (Rational): The maximal value of slope when increasing (should be > 0), or nil if none

  • iSlopeDown (Rational): The minimal value of slope when decreasing (should be < 0), or nil if none



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/WSK/Functions.rb', line 262

def apply_damping(iSlopeUp, iSlopeDown)
  if ((iSlopeUp != nil) and
      (iSlopeUp <= 0))
    log_err "Upward slope (#{iSlopeUp}) has to be > 0"
  elsif ((iSlopeDown != nil) and
         (iSlopeDown >= 0))
    log_err "Downward slope (#{iSlopeDown}) has to be < 0"
  else
    case @Function[:FunctionType]
    when FCTTYPE_PIECEWISE_LINEAR
      # Keep the first point
      lNewPoints = [ @Function[:Points][0] ]
      lIdxSegment = 0
      while (lIdxSegment < @Function[:Points].size - 1)
        # Compute the slope of this segment
        #puts "lIdxSegment=#{lIdxSegment}/#{@Function[:Points].size} Points=[ [ #{sprintf('%.2f',@Function[:Points][lIdxSegment][0])}, #{sprintf('%.2f',@Function[:Points][lIdxSegment][1])} ], [ #{sprintf('%.2f',@Function[:Points][lIdxSegment+1][0])}, #{sprintf('%.2f',@Function[:Points][lIdxSegment+1][1])} ] ]"
        lSegmentSlope = (@Function[:Points][lIdxSegment+1][1]-@Function[:Points][lIdxSegment][1])/(@Function[:Points][lIdxSegment+1][0]-@Function[:Points][lIdxSegment][0])
        #puts "lIdxSegment=#{lIdxSegment}/#{@Function[:Points].size} Slope=#{sprintf('%.2f',lSegmentSlope)} (#{lSegmentSlope.precs.inspect})"
        if (((lSegmentSlope > 0) and
             (iSlopeUp != nil) and
             (lSegmentSlope > iSlopeUp)) or
            ((lSegmentSlope < 0) and
             (iSlopeDown != nil) and
             (lSegmentSlope < iSlopeDown)))
          # Choose the correct damping slope depending on the direction
          lSlope = nil
          if (lSegmentSlope > 0)
            lSlope = iSlopeUp
          else
            lSlope = iSlopeDown
          end
          # We have to apply damping starting the beginning of this segment.
          # Find the next intersection between the damped segment and our function.
          # The abscisse of the intersection
          lIntersectX = nil
          # A constant for the next loop
          lDampedSegmentOffsetY = @Function[:Points][lIdxSegment][1] - @Function[:Points][lIdxSegment][0]*lSlope
          lIdxSegmentIntersect = lIdxSegment + 1
          while (lIdxSegmentIntersect < @Function[:Points].size - 1)
            # Find if there is an intersection
            lSegmentIntersectDistX = @Function[:Points][lIdxSegmentIntersect+1][0] - @Function[:Points][lIdxSegmentIntersect][0]
            lSegmentIntersectDistY = @Function[:Points][lIdxSegmentIntersect+1][1] - @Function[:Points][lIdxSegmentIntersect][1]
            lIntersectX = ((lDampedSegmentOffsetY - @Function[:Points][lIdxSegmentIntersect][1])*lSegmentIntersectDistX + @Function[:Points][lIdxSegmentIntersect][0]*lSegmentIntersectDistY)/(lSegmentIntersectDistY - lSlope*lSegmentIntersectDistX)
            # Is lIntersectX among our range ?
            if ((lIntersectX >= @Function[:Points][lIdxSegmentIntersect][0]) and
                (lIntersectX <= @Function[:Points][lIdxSegmentIntersect+1][0]))
              # We have an intersection in the segment beginning at point n. lIdxSegmentIntersect, exactly at abscisse lIntersectX.
              break
            else
              # Erase it as we will test for it after the loop
              lIntersectX = nil
            end
            lIdxSegmentIntersect += 1
          end
          # Here, lIdxSegmentIntersect can point to the last point if no intersection was found
          if (lIntersectX == nil)
            # We could not find any intersection
            # We consider adding a point following the damped slope till the end of the function
            lIntersectX = @Function[:Points][-1][0]
          end
          # Add the intersecting point (could be the last one)
          lIntersectPoint = [ lIntersectX, (lIntersectX - @Function[:Points][lIdxSegment][0])*lSlope + @Function[:Points][lIdxSegment][1] ]
          #puts "lIntersectX=#{lIntersectX.to_f} @Function[:Points][lIdxSegment][0]=#{@Function[:Points][lIdxSegment][0].to_f} lSlope=#{lSlope.to_f} @Function[:Points][lIdxSegment][1]=#{@Function[:Points][lIdxSegment][1].to_f} lIntersectPoint[1]=#{lIntersectPoint[1].to_f}"
          lNewPoints << lIntersectPoint
          # Continue after this intersection (we create also the intersecting point on our old points by modifying them)
          @Function[:Points][lIdxSegmentIntersect] = lIntersectPoint
          lIdxSegment = lIdxSegmentIntersect
        else
          # The slope is ok, keep this segment as it is
          lNewPoints << @Function[:Points][lIdxSegment+1]
          lIdxSegment += 1
        end
      end
      # Replace our points with new ones
      @Function[:Points] = lNewPoints
    else
      log_err "Unknown function type: #{@Function[:FunctionType]}"
    end
  end
  optimize
end

#apply_map_function(iMapFunction) ⇒ Object

Apply a mapping function to this function.

Parameters
  • iMapFunction (Function): The mapping function



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/WSK/Functions.rb', line 429

def apply_map_function(iMapFunction)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    case iMapFunction.function_data[:FunctionType]
    when FCTTYPE_PIECEWISE_LINEAR
      # Both functions are piecewise linear
      # Algorithm:
      # * For each segment of our function:
      #   * We look at the segments from the map function.
      #   * For each found segment:
      # *** We find at which abscisses this segment will change values
      # *** We change the sub-segment between those abscisses
      lPoints = @Function[:Points]
      lMapPoints = iMapFunction.function_data[:Points]
      lNewPoints = []
      lIdxSegment = 0
      while (lIdxSegment < lPoints.size-1)
        lBeginX = lPoints[lIdxSegment][0]
        lBeginY = lPoints[lIdxSegment][1]
        lEndX = lPoints[lIdxSegment+1][0]
        lEndY = lPoints[lIdxSegment+1][1]
        # The direction in which we are going to look for the map segments
        lIncMapSegment = nil
        if (lEndY >= lBeginY)
          lIncMapSegment = true
        else
          lIncMapSegment = false
        end
        # Find the map function's segment containing the beginning of our segment
        lIdxMapSegment = 0
        if (lBeginY == lMapPoints[-1][0])
          lIdxMapSegment = lMapPoints.size - 2
        else
          while (lBeginY >= lMapPoints[lIdxMapSegment+1][0])
            lIdxMapSegment += 1
          end
        end
        # Compute the new value of our segment beginning
        lNewBeginY = lMapPoints[lIdxMapSegment][1] + ((lMapPoints[lIdxMapSegment+1][1]-lMapPoints[lIdxMapSegment][1])*(lBeginY-lMapPoints[lIdxMapSegment][0]))/(lMapPoints[lIdxMapSegment+1][0]-lMapPoints[lIdxMapSegment][0])
        lNewPoints << [ lBeginX, lNewBeginY ]
        # Get the next map segments unless we reach our segment's end
        # !!! Find the next map segment according to the direction
        if (lIncMapSegment)
          while (lEndY > lMapPoints[lIdxMapSegment+1][0])
            # We have a new map segment to consider in our segment
            # Find the absciss at which our Y coordinates get the value lMapPoints[lIdxMapSegment+1][0]
            lNewSegmentX = lBeginX + ((lEndX-lBeginX)*(lMapPoints[lIdxMapSegment+1][0] - lBeginY))/(lEndY-lBeginY)
            lNewPoints << [ lNewSegmentX, lMapPoints[lIdxMapSegment+1][1] ]
            lIdxMapSegment += 1
          end
          # Our segment ends before next map segment
        else
          while (lEndY < lMapPoints[lIdxMapSegment][0])
            # We have a new map segment to consider in our segment
            # Find the absciss at which our Y coordinates get the value lMapPoints[lIdxMapSegment][0]
            lNewSegmentX = lBeginX + ((lEndX-lBeginX)*(lMapPoints[lIdxMapSegment][0] - lBeginY))/(lEndY-lBeginY)
            lNewPoints << [ lNewSegmentX, lMapPoints[lIdxMapSegment][1] ]
            lIdxMapSegment -= 1
          end
          # Our segment ends before previous map segment
        end
        # Write the segment end if it is the last one (otherwise it will be written by the next iteration)
        if (lIdxSegment == lPoints.size-2)
          lNewEndY = lMapPoints[lIdxMapSegment][1] + ((lMapPoints[lIdxMapSegment+1][1]-lMapPoints[lIdxMapSegment][1])*(lEndY-lMapPoints[lIdxMapSegment][0]))/(lMapPoints[lIdxMapSegment+1][0]-lMapPoints[lIdxMapSegment][0])
          lNewPoints << [ lEndX, lNewEndY ]
        end
        lIdxSegment += 1
      end
      # Replace with new points
      @Function[:Points] = lNewPoints
    else
      log_err "Unknown function type: #{@Function[:FunctionType]}"
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
  optimize
end

#apply_on_volume(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB) ⇒ Object

Apply the function on the volume of a raw buffer

Parameters
  • iInputData (WSK::Model::InputData): The input data

  • oOutputData (WSK::Model::DirectStream): The output data

  • iIdxBeginSample (Integer): Index of the first sample beginning the volume transformation

  • iIdxEndSample (Integer): Index of the last sample ending the volume transformation

  • iUnitDB (Boolean): Are function values to be interpreted as DB units ?



178
179
180
181
182
183
184
185
186
187
# File 'lib/WSK/Functions.rb', line 178

def apply_on_volume(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB)
  prepareFunctionUtils
  lCFunction = @FunctionUtils.createCFunction(@Function, iIdxBeginSample, iIdxEndSample)
  lIdxBufferSample = iIdxBeginSample
  iInputData.each_raw_buffer(iIdxBeginSample, iIdxEndSample) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
    prepareVolumeUtils
    oOutputData.pushRawBuffer(@VolumeUtils.applyVolumeFct(lCFunction, iInputRawBuffer, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels, iNbrSamples, lIdxBufferSample, iUnitDB))
    lIdxBufferSample += iNbrSamples
  end
end

#convert_to_db(iMaxYValue) ⇒ Object

Convert the Y units in DB equivalent

Parameters
  • iMaxYValue (Rational): Maximal Y value



226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/WSK/Functions.rb', line 226

def convert_to_db(iMaxYValue)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    # Prepare variables for log computations
    @Log2 = Math::log(2).to_r
    @LogMax = value_log(iMaxYValue)
    @Function[:Points].each do |ioPoint|
      ioPoint[1] = value_val_2_db_Internal(ioPoint[1])
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
end

#divide_by(iFactor) ⇒ Object

Divide values by a given factor

Parameters
  • iFactor (Rational): Factor to divide by



211
212
213
214
215
216
217
218
219
220
# File 'lib/WSK/Functions.rb', line 211

def divide_by(iFactor)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    @Function[:Points].each do |ioPoint|
      ioPoint[1] /= iFactor
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
end

#divide_by_function(iDivFunction) ⇒ Object

Divide this function by another function

Parameters
  • iDivFunction (Function): The function that divides



578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
# File 'lib/WSK/Functions.rb', line 578

def divide_by_function(iDivFunction)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    case iDivFunction.function_data[:FunctionType]
    when FCTTYPE_PIECEWISE_LINEAR
      lNewPoints = []
      unionXWithFunction_PiecewiseLinear(iDivFunction) do |iX, iY, iOtherY|
        if (iY == nil)
          lNewPoints << [ iX, 0 ]
        elsif (iOtherY == nil)
          lNewPoints << [ iX, 0 ]
        else
          lNewPoints << [ iX, iY / iOtherY ]
        end
      end
      # Replace with new points
      @Function[:Points] = lNewPoints
    else
      log_err "Unknown function type: #{@Function[:FunctionType]}"
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
  optimize
end

#draw(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB, iMedianValue) ⇒ Object

Draw the function into a raw buffer

Parameters
  • iInputData (WSK::Model::InputData): The input data

  • oOutputData (WSK::Model::DirectStream): The output data

  • iIdxBeginSample (Integer): Index of the first sample beginning the volume transformation

  • iIdxEndSample (Integer): Index of the last sample ending the volume transformation

  • iUnitDB (Boolean): Are function values to be interpreted as DB units ?

  • iMedianValue (Integer): Median value to draw function ratio of 1.



198
199
200
201
202
203
204
205
# File 'lib/WSK/Functions.rb', line 198

def draw(iInputData, oOutputData, iIdxBeginSample, iIdxEndSample, iUnitDB, iMedianValue)
  prepareFunctionUtils
  lCFunction = @FunctionUtils.createCFunction(@Function, iIdxBeginSample, iIdxEndSample)
  oOutputData.each_buffer(iIdxBeginSample, iIdxEndSample) do |iIdxBeginBufferSample, iIdxEndBufferSample|
    prepareVolumeUtils
    oOutputData.pushRawBuffer(@VolumeUtils.drawVolumeFct(lCFunction, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels, iIdxEndBufferSample-iIdxBeginBufferSample+1, iIdxBeginBufferSample, iUnitDB, iMedianValue))
  end
end

#function_dataObject

Get the internal function data

Return
  • map<Symbol,Object>: The internal function data



608
609
610
# File 'lib/WSK/Functions.rb', line 608

def function_data
  return @Function
end

#get_boundsObject

Get the function bounds

Return
  • Rational: Minimal X

  • Rational: Minimal Y

  • Rational: Maximal X

  • Rational: Maximal Y



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/WSK/Functions.rb', line 366

def get_bounds
  rMinX = nil
  rMinY = nil
  rMaxX = nil
  rMaxY = nil

  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    rMinX = @Function[:Points][0][0]
    rMaxX = @Function[:Points][-1][0]
    @Function[:Points].each do |iPoint|
      if (rMinY == nil)
        rMinY = iPoint[1]
        rMaxY = iPoint[1]
      else
        if (rMinY > iPoint[1])
          rMinY = iPoint[1]
        end
        if (rMaxY < iPoint[1])
          rMaxY = iPoint[1]
        end
      end
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end

  return rMinX, rMinY, rMaxX, rMaxY
end

#invert_abscissesObject

Invert the abscisses of a function



345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/WSK/Functions.rb', line 345

def invert_abscisses
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    lNewPoints = []
    lMinMaxX = @Function[:Points][0][0] + @Function[:Points][-1][0]
    @Function[:Points].reverse_each do |iPoint|
      lNewPoints << [lMinMaxX - iPoint[0], iPoint[1]]
    end
    @Function[:Points] = lNewPoints
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
end

#read_from_file(iFileName) ⇒ Object

Read from a file

Parameters
  • iFileName (String): File name



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/WSK/Functions.rb', line 77

def read_from_file(iFileName)
  lStrFunction = nil
  if (File.exists?(iFileName))
    File.open(iFileName, 'r') do |iFile|
      lStrFunction = iFile.read
    end
  else
    raise RuntimeError.new("Missing file #{iFileName} to load function.")
  end
  begin
    @Function = eval(lStrFunction)
  rescue Exception
    raise RuntimeError.new("Invalid function specified in file #{iFileName}: #{$!}")
  end
  convertDataTypes
  optimize
end

#read_from_input_volume(iInputData, iIdxBeginSample, iIdxEndSample, iInterval, iRMSRatio) ⇒ Object

Read a function from the volume of an input data

Parameters
  • iInputData (WSK::Model::InputData): The input data

  • iIdxBeginSample (Integer): Index of the first sample beginning the volume reading

  • iIdxEndSample (Integer): Index of the last sample ending the volume reading

  • iInterval (Integer): The number of samples used as an interval in measuring the volume

  • iRMSRatio (Float): The ratio of RMS measure vs Peak measure (0.0 = only peak, 1.0 = only RMS)



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/WSK/Functions.rb', line 103

def read_from_input_volume(iInputData, iIdxBeginSample, iIdxEndSample, iInterval, iRMSRatio)
  @Function = {
    :FunctionType => FCTTYPE_PIECEWISE_LINEAR,
    :Points => []
  }
  # Profile
  prepareVolumeUtils
  lIdxCurrentSample = iIdxBeginSample
  while (lIdxCurrentSample <= iIdxEndSample)
    lIdxCurrentEndSample = lIdxCurrentSample + iInterval - 1
    if (lIdxCurrentEndSample > iIdxEndSample)
      lIdxCurrentEndSample = iIdxEndSample
    end
    lRawBuffer = ''
    iInputData.each_raw_buffer(lIdxCurrentSample, lIdxCurrentEndSample, :nbr_samples_prefetch => iIdxEndSample-lIdxCurrentSample) do |iInputRawBuffer, iNbrSamples, iNbrChannels|
      lRawBuffer += iInputRawBuffer
    end
    # Profile this buffer
    lChannelLevelValues = @VolumeUtils.measureLevel(lRawBuffer, iInputData.Header.NbrBitsPerSample, iInputData.Header.NbrChannels, lIdxCurrentEndSample - lIdxCurrentSample + 1, iRMSRatio)
    # Combine the channel levels based on the RMS ratio also
    lMaxValue = Rational(0, 1)
    lRMSValue = Rational(0, 1)
    lChannelLevelValues.each do |iLevelValue|
      if (iLevelValue > lMaxValue)
        lMaxValue = iLevelValue
      end
      lRMSValue += iLevelValue*iLevelValue
    end
    lRMSValue = Math.sqrt(lRMSValue/lChannelLevelValues.size).to_r
    lLevelValue = lRMSValue*(iRMSRatio.to_r) + lMaxValue*(Rational(1)-iRMSRatio.to_r)
    #log_debug "[#{lIdxCurrentSample} - #{lIdxCurrentEndSample}] - Level: #{lLevelValue}"
    # If intervals are of length 1, the function is exactly the profile: no need to make intermediate points
    if (lIdxCurrentEndSample == lIdxCurrentSample)
      @Function[:Points] << [ Rational(lIdxCurrentSample), lLevelValue]
      lIdxCurrentSample += 1
    else
      # Complete the function
      if (@Function[:Points].empty?)
        # First points: add also the point 0
        @Function[:Points] = [ [ Rational(0, 1), lLevelValue] ]
      end
      # Add a point to the function in the middle of this interval
      lPointX = lIdxCurrentSample - iIdxBeginSample + Rational(lIdxCurrentEndSample - lIdxCurrentSample + 1, 2)
      @Function[:Points] << [lPointX, lLevelValue]
      # Increment the cursor
      lIdxCurrentSample = lIdxCurrentEndSample + 1
      if (lIdxCurrentSample == iIdxEndSample + 1)
        # The last point: add the ending one
        @Function[:Points] << [Rational(iIdxEndSample - iIdxBeginSample, 1), lLevelValue]
      end
    end
    $stdout.write("#{(lIdxCurrentSample*100)/(iIdxEndSample - iIdxBeginSample + 1)} %\015")
    $stdout.flush
  end
  optimize
end

#remove_noise_abscisses(iMinDistance) ⇒ Object

Remove intermediate abscisses that are too close to each other

Parameters
  • iMinDistance (Rational): Minimal distance for abscisses triplets to have



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/WSK/Functions.rb', line 512

def remove_noise_abscisses(iMinDistance)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    lNewPoints = [ @Function[:Points][0] ]
    lIdxPoint = 0
    while (lIdxPoint < @Function[:Points].size - 1)
      # Now we skip the next last point among iMinDistance range
      lPointX = @Function[:Points][lIdxPoint][0]
      lIdxOtherPoint = lIdxPoint + 1
      while ((lIdxOtherPoint < @Function[:Points].size) and
             (@Function[:Points][lIdxOtherPoint][0] - lPointX < iMinDistance))
        lIdxOtherPoint += 1
      end
      # Either lIdxOtherPoint is beyond the end, or it points to the first point that is beyond iMinDistance
      # We add the previous point if it is not already ours
      if (lIdxOtherPoint-1 > lIdxPoint)
        lNewPoints << @Function[:Points][lIdxOtherPoint-1]
        # And we continue searching from this new added point
        lIdxPoint = lIdxOtherPoint-1
      else
        # It is our point, continue on to the next one
        lNewPoints << @Function[:Points][lIdxOtherPoint]
        lIdxPoint = lIdxOtherPoint
      end
    end
    @Function[:Points] = lNewPoints
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
  optimize
end

#round_to_precision(iPrecisionX, iPrecisionY) ⇒ Object

Round values to a given precision

Parameters
  • iPrecisionX (Rational): The desired precision for X values (1000 will round to E-3)

  • iPrecisionY (Rational): The desired precision for Y values (1000 will round to E-3)



245
246
247
248
249
250
251
252
253
254
255
# File 'lib/WSK/Functions.rb', line 245

def round_to_precision(iPrecisionX, iPrecisionY)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    @Function[:Points] = @Function[:Points].map do |iPoint|
      next [ (Rational((iPoint[0]*iPrecisionX).round, 1))/iPrecisionX, (Rational((iPoint[1]*iPrecisionY).round, 1))/iPrecisionY ]
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
  optimize
end

#set(iHashFunction) ⇒ Object

Set directly a function from a hash

Parameters
  • iHashFunction (map<Symbol,Object>): The hashed function



164
165
166
167
168
# File 'lib/WSK/Functions.rb', line 164

def set(iHashFunction)
  @Function = iHashFunction
  convertDataTypes
  optimize
end

#substract_function(iSubFunction) ⇒ Object

Substract a function to this function

Parameters
  • iSubFunction (Function): The function to substract



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
# File 'lib/WSK/Functions.rb', line 548

def substract_function(iSubFunction)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    case iSubFunction.function_data[:FunctionType]
    when FCTTYPE_PIECEWISE_LINEAR
      lNewPoints = []
      unionXWithFunction_PiecewiseLinear(iSubFunction) do |iX, iY, iOtherY|
        if (iY == nil)
          lNewPoints << [ iX, -iOtherY ]
        elsif (iOtherY == nil)
          lNewPoints << [ iX, iY ]
        else
          lNewPoints << [ iX, iY - iOtherY ]
        end
      end
      # Replace with new points
      @Function[:Points] = lNewPoints
    else
      log_err "Unknown function type: #{@Function[:FunctionType]}"
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
  optimize
end

#value_log(iValue) ⇒ Object

Compute the log of a function value.

Parameters
  • iValue (Rational): The value



616
617
618
# File 'lib/WSK/Functions.rb', line 616

def value_log(iValue)
  return Math::log(iValue).to_r
end

#value_val_2_db(iValue, iMaxValue) ⇒ Object

Compute a DB value out of a ratio using function values

Parameters
  • iValue (Rational): The value

  • iMaxValue (Rational): The maximal value

Return
  • Rational: Its corresponding db



627
628
629
630
631
632
# File 'lib/WSK/Functions.rb', line 627

def value_val_2_db(iValue, iMaxValue)
  @Log2 = Math::log(2).to_r
  @LogMax = value_log(iMaxValue)

  return value_val_2_db_Internal(iValue)
end

#write_to_file(iFileName, iParams = {}) ⇒ Object

Write the function to a file

Parameters
  • iFileName (String): File name to write

  • iParams (map<Symbol,Object>): Additional parameters [optional = {}]:

    • :floats (Boolean): Do we write Float values ? [optional = false]



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/WSK/Functions.rb', line 402

def write_to_file(iFileName, iParams = {})
  lParams = {
    # Default value
    :floats => false
  }.merge(iParams)
  case @Function[:FunctionType]
  when FCTTYPE_PIECEWISE_LINEAR
    require 'pp'
    lData = @Function
    if (lParams[:floats])
      # Convert to Floats
      lData[:Points].map! do |iPoint|
        next [ iPoint[0].to_f, iPoint[1].to_f ]
      end
    end
    File.open(iFileName, 'w') do |oFile|
      oFile.write(lData.pretty_inspect)
    end
  else
    log_err "Unknown function type: #{@Function[:FunctionType]}"
  end
end