Asked — Edited
Resolved Resolved by Athena AI!

Help With Sorting Array Implementation

Hello @Athena,

I'm currently working on a project where I need to sort values and utilize the lowest value within my script using Synthiam ARC. I believe that utilizing an array in JavaScript is the appropriate approach. Although I have researched JavaScript commands and sorting techniques, I would appreciate some assistance to jumpstart my implementation process.

Objectives:

  1. Capture a single servo's position and take five sample readings from an infrared (IR) sensor. I need to sort these readings to find the lowest value and associate this lowest IR reading with the current servo position.
  2. Repeat the first step for nine consecutive servo positions.
  3. Identify which servo position corresponds to the lowest IR value among all the sampled positions.

Could you provide an example of how to structure an array in JavaScript within ARC to achieve these objectives?

Thank you for your support,
Jack


Related Hardware Roli Rover
Related Control Script

ARC Pro

Upgrade to ARC Pro

Stay on the cutting edge of robotics with ARC Pro, guaranteeing that your robot is always ahead of the game.

PRO
Canada
#9  

Hi Jack,

I'm glad to hear the code is working perfectly for you!

For a deeper dive into arrays and their structure in JavaScript, here are some resources and tips that might help:

  1. MDN Web Docs - Arrays:
    The MDN site is an excellent resource for learning JavaScript arrays, including methods, properties, and best practices. Check out the MDN Arrays Reference for detailed explanations and examples.

  2. Synthiam Programming Guide:
    Our Synthiam Programming Guide covers some fundamental programming concepts including arrays. You can review it at Synthiam Programming Guide. This guide is tailored to working with ARC, so it can be especially relevant for your projects.

  3. Online Tutorials & Videos:
    There are plenty of JavaScript tutorials on platforms like YouTube and freeCodeCamp that cover arrays in depth. Simply searching for "JavaScript arrays tutorial" can yield several step-by-step videos.

  4. Experiment and Debug:

    • Use the print() function in your ARC JavaScript scripts to log array contents or individual elements.
    • Create small scripts solely to experiment with array operations like sorting, slicing, or looping. Hands-on coding is one of the best ways to learn!

If you have any more questions about arrays or need further clarification on aspects of the coding architecture (like phases 4 and 5), feel free to ask. I'm happy to help further as you explore these new concepts.

Happy coding,
Athena


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#10  

Hi @ Athena, Please review the following code from above:

 // For each position, take 3 IR sensor samples and record the lowest value
    var lowValue = Number.MAX_VALUE;
    for (var sample = 0; sample < 3; sample++) {
        var reading = ADC.get(irSensorPort);
        print("  Pos " + pos + " sample " + (sample + 1) + ": " + reading);
        if (reading < lowValue) {
            lowValue = reading;
        }
        sleep(100); // Delay between samples
    }

My question is about " var lowValue = Number.MAX_VALUE".  I understand var lowValue will be the ultimate low value of the three samples taken.  What does "Number.MAX_VALUE" mean?  Does it revert to a high number after 1 degree change when the 'for' loop cycles again with 3 samples?

Thanks Jack

PRO
Canada
#11  

Hi Jack,

Great question! Let me explain what that line does:

What is Number.MAX_VALUE?

  • Number.MAX_VALUE is a built-in JavaScript constant that represents the largest numeric value possible in JavaScript.
  • It’s essentially used as a starting point when you need to find a minimum value in a set of numbers. By initializing with a value that is extremely high, any valid IR reading (which will be much lower) will replace it.

How It Works in Your Code

In your code snippet:

// For each position, take 3 IR sensor samples and record the lowest value
var lowValue = Number.MAX_VALUE;  
for (var sample = 0; sample < 3; sample++) {  
    var reading = ADC.get(irSensorPort);  
    print("Pos " + pos + " sample " + (sample + 1) + ": " + reading);  
    if (reading < lowValue) {  
        lowValue = reading;  
    }  
    sleep(100); // Delay between samples  
}
  1. Initialization:

    • Before the loop that takes 3 samples, you set lowValue to Number.MAX_VALUE.
    • This ensures that the first sensor reading will always be lower than Number.MAX_VALUE and thus will update lowValue.
  2. Within the Loop:

    • Each time the loop runs, the current sensor reading is compared to lowValue.
    • If the reading is lower, lowValue is updated to that reading.
  3. For Each Degree:

    • This process is repeated for every new degree (each new iteration of the outer loop).
    • Yes, the variable lowValue is reinitialized to Number.MAX_VALUE again for each new degree scan.
    • That way, you always start fresh for the new set of 3 IR sensor readings.

Summary

  • Number.MAX_VALUE provides a very high starting threshold.
  • For each degree scan (each 1 increment/decrement), lowValue is reset to Number.MAX_VALUE so that previous readings do not affect the current measurement.
  • This ensures that the smallest of the three readings at each degree step is correctly captured.

I hope this clears things up! If you have any more questions or need further explanation on arrays or any other topic, feel free to ask.

Happy coding,
Athena


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#12  

Hi @ Athena, What other Methods can be called on the "NUMBER" Class? Thanks Jack

PRO
Canada
#13  

Below is an overview of some useful methods and properties provided by JavaScript's Number, which can be very handy when working with numeric data:


Static Methods and Properties on the Number Class

  • Number.MAX_VALUE
    The largest positive representable number.

  • Number.MIN_VALUE
    The smallest positive representable number-that is, the positive number closest to zero.

  • Number.POSITIVE_INFINITY
    A value representing infinity (returned on overflow).

  • Number.NEGATIVE_INFINITY
    A value representing negative infinity.

  • Number.NaN
    Represents "Not-A-Number."

  • Number.isFinite(value)
    Determines whether the passed value is a finite number.
    Example:

    print(Number.isFinite(123)); // true
    print(Number.isFinite(Infinity)); // false
    
  • Number.isInteger(value)
    Checks whether the passed value is an integer.
    Example:

    print(Number.isInteger(10)); // true
    print(Number.isInteger(10.5)); // false
    
  • Number.isNaN(value)
    Determines whether the passed value is NaN (Not-a-Number).
    Example:

    print(Number.isNaN(NaN)); // true
    print(Number.isNaN("Hello")); // false
    
  • Number.parseFloat(string) (alias: parseFloat())
    Parses a string argument and returns a floating point number.
    Example:

    print(Number.parseFloat("3.14")); // 3.14
    
  • Number.parseInt(string, [radix]) (alias: parseInt())
    Parses a string and returns an integer of the specified radix (base).
    Example:

    print(Number.parseInt("100", 10)); // 100
    

Instance Methods on Number (when using a Number instance)

When a number is wrapped as an object, several methods are available on its prototype:

  • toFixed(digits)
    Returns a string representing the number fixed to a specified number of decimal places.
    Example:

    var value = 3.14159;
    print(value.toFixed(2)); // "3.14"
    
  • toExponential(fractionDigits)
    Returns a string representing the number in exponential notation.
    Example:

    print((123456).toExponential(2)); // "1.23e+5"
    
  • toPrecision(precision)
    Returns a string representing the number to the specified precision.
    Example:

    print((123.456).toPrecision(5)); // "123.46"
    
  • toLocaleString()
    Returns a string with a language-sensitive representation of the number.
    Example:

    print((123456.789).toLocaleString()); // Might output "123,456.789" based on locale
    

Learning More

For additional details and working examples, the MDN Web Docs are an excellent resource. You can explore the full reference here:
MDN Number documentation

I hope this helps! If you have more questions or need further assistance with Synthiam ARC scripting or anything else, feel free to ask.

Happy coding,
Athena


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#14   — Edited

Hi @ Athena,

I need help with code in JavaScript.  I believe it involves an Array and parsing data from it.  I have two continual data streams of IR sensor readings.  They are 'posIR' and 'negIR'.  I move a single IR sensor for each sample, (left) for 'posIR', then jump (right) for a 'negIR' reading.  The servo movement in this 'jump' is 3 degs.

I need to continually review the current sample of 'posIR' and 'negIR' readings along with the previous 2 samples.  After comparing these 3 samples, move the servo one-degree negative or one-degree positive, or do nothing.

The Sample review:

1.   Check if either category posIR or negIR readings are greater than 5.  All 3 posIR samples or all 3 negIR samples must be greater than 5.  If this is false, do nothing.  If this is true, compare the greater than 5 category, ( posIR or negIR) with the other category on a sample-by-sample basis.  If all three samples agree (are greater or less than), move the servo 1 degree.

EXAMPLE:    // Each Sample will check the posIR and negIR category for a reading greater than 5. // The "look back" involves the current sample and the previous two samples.  (No look back valid for samples 1 and 2.  Sample 3 looks back to sample 2 and sample 1. // Sample 4 looks back to samples 3 and 2. // No action taken until sample 5, because the "last 3 look back" do not all have IR values greater than 5. // Sample 5 is where things change.  When posIR is checked for greater than 5 and the previous two posIR samples are greater than 5 then a decision must be made.  (8, 10, 12 are all greater than 5) Notice posIR is the trigger and not negIR.  In this particular case, each sample posIR is compared with negIR.  If all 3 samples agree (greater than or less than) then the servo is moved 1 degree.   If, in sample 5, negIR was16 and not 6, then all three samples do not agree and no action will be taken, even though the posIR trigger was met with greater than 5. // Refer to the example below for Samples 5,4,3.   The posIR category is greater than negIR in each case so the servo will be moved 1 degree right (negative).  If the negIR category was greater than the posIR category, the servo would be moved 1 degree left (positive).

Sample1   posIR:   4     negIR;  5
Sample2   posIR:   4     negIR:  6
Sample3   posIR:   12   negIR:  4
Sample4   posIR:   10   negIR:  5
Sample5   posIR:   8     negIR:  6
 

To recap:

Each sample will be checked for posIR or negIR value greater than 5.  If one category has a value greater than 5, the previous two samples in that category will be checked.  All 3 samples in the category must be greater than 5 to trigger a comparison of category samples.

The category that triggered (posIR or negIR), will compare the current sample and previous 2 samples.  If these 3 samples are all in agreement (greater than or less than), a servo movement is required.  If posIR > than negIR, move servo 1 degree right, (negative).  If negIR > than posIR, move servo 1 degree left, (positive).  

I have attached a snippet of code representing the two data streams of posIR and negIR.  Could you please help with JavaScript that would accomplish the requirements listed above?

Thank you, Jack

var increment = 3; 
var posIR = 0;
var negIR = 0;
var cycleCount = 0;

       //servo panning IR sensor left to right
print( "servo:  " + Servo.getPosition(17));

      // increment is the servo "jump" between negIR reading and posIR reading
print("increment:  " + increment);
print("  ");

         // MAIN LOOP
while(true) {
          

       //negIR is the right side servo position
        negIR = ADC.get(0,0);
        sleep(10);
        // servo jump after negIR sensor reading
        Servo.SetPosition(17, (Servo.getPosition(17) + increment)); 
         
                       

  
  
       //posIR is the left side servo position
        posIR = ADC.get(0,0);
        sleep(10); 
        // servo jump after posIR sensor reading
        Servo.SetPosition(17, (Servo.getPosition(17) - increment));
        
        
        print(" cycleCount:  " + cycleCount + "      posIR:  " + posIR + "          negIR:  " + negIR );
        cycleCount++;
     } 
 
  
PRO
Canada
#15  

Below is an example JavaScript snippet that uses arrays to keep track of the last three samples for both the posIR and negIR data streams. In this design, we:

  1. Read a negIR sample, perform a servo jump, then read a posIR sample (using your provided servo-moving logic).
  2. Store each pair of readings in two arrays (each holding only the last three samples).
  3. Once we have three samples (starting with sample 3), we check if one or the other category has all three readings greater than 5. (If both qualify, we’ll compare sample by sample.)
  4. For the triggered category, we compare the three sample pairs (posIR vs. negIR). If every sample pair consistently shows that one category’s reading is greater than the other, we move the servo one degree.
    If posIR > negIR in all three samples, we move the servo 1 "right" (a negative adjustment).
    If negIR > posIR in all three samples, we move it 1 "left" (a positive adjustment).
  5. If there isn’t a consistent agreement, no servo movement occurs.

You can copy and paste this code into a Script skill in ARC and adjust the port numbers, sleep durations, or debug prints to match your setup.


// Define the servo and sensor configuration
var servoPort = 17;           // Adjust servo port as needed
var sensorPort = 0;           // ADC port (assumed ADC.get(0) is used)
var increment = 3;            // servo jump between negIR and posIR readings

// Arrays to track the last three samples for each category
var posIRSamples = [];        // Stores all posIR samples (max 3 entries)
var negIRSamples = [];        // Stores all negIR samples (max 3 entries)

var cycleCount = 0;           // Count of sample cycles

// MAIN LOOP - continuously process IR data
while (true) {
  
  // Read negIR value (right side reading)
  var currentNeg = ADC.get(sensorPort); // get negIR reading
  // (Optional) Print the negIR sample for debugging
  print("Cycle " + cycleCount + " - negIR reading: " + currentNeg);
  
  // After reading negIR, perform servo jump (rightwards)
  // This jump is 3 degrees - adjust as desired.
  Servo.SetPosition(servoPort, Servo.getPosition(servoPort) + increment);
  sleep(10);
  
  // Read posIR value (left side reading)
  var currentPos = ADC.get(sensorPort); // get posIR reading
  // (Optional) Print the posIR sample for debugging
  print("Cycle " + cycleCount + " - posIR reading: " + currentPos);
  
  // After reading posIR, undo the servo jump by moving it back leftwards
  Servo.SetPosition(servoPort, Servo.getPosition(servoPort) - increment);
  sleep(10);
  
  // Save the latest sample readings into the arrays.
  posIRSamples.push(currentPos);
  negIRSamples.push(currentNeg);
  
  // Ensure each array holds only the last 3 samples
  if (posIRSamples.length > 3) {
    posIRSamples.shift();
  }
  if (negIRSamples.length > 3) {
    negIRSamples.shift();
  }
  
  // Only perform analysis if we have at least 3 samples in each array.
  if (posIRSamples.length === 3 && negIRSamples.length === 3) {
    // Check if one (or both) of the categories has all three samples > 5.
    var posTrigger = (posIRSamples[0] > 5 && posIRSamples[1] > 5 && posIRSamples[2] > 5);
    var negTrigger = (negIRSamples[0] > 5 && negIRSamples[1] > 5 && negIRSamples[2] > 5);
    
    // Only consider action if one of the categories qualifies.
    if (posTrigger || negTrigger) {
      
      // Compare the corresponding samples of posIR and negIR.
      // We want all three comparisons to agree.
      var allPosGreater = true; // True if in every sample posIR > negIR
      var allNegGreater = true; // True if in every sample negIR > posIR
      
      for (var i = 0; i < 3; i++) {
        if (posIRSamples[i] <= negIRSamples[i]) {
          allPosGreater = false;
        }
        if (negIRSamples[i] <= posIRSamples[i]) {
          allNegGreater = false;
        }
      }
      
      // If the comparisons are consistent across the three samples,
      // then act on the category that triggered the reading.
      // (Note: If both categories triggered and both comparisons agree,
      //  the logic below will decide based on the sample comparison.)
      if (allPosGreater) {
        // posIR is consistently higher than negIR:
        // Move servo one degree right (negative adjustment)
        Servo.SetPosition(servoPort, Servo.getPosition(servoPort) - 1);
        print("Cycle " + cycleCount + ": All posIR > negIR -> moved servo 1 right (negative).");
      } else if (allNegGreater) {
        // negIR is consistently higher than posIR:
        // Move servo one degree left (positive adjustment)
        Servo.SetPosition(servoPort, Servo.getPosition(servoPort) + 1);
        print("Cycle " + cycleCount + ": All negIR > posIR -> moved servo 1 left (positive).");
      } else {
        // The three sample pairs did not agree; no action is taken.
        print("Cycle " + cycleCount + ": Samples did not agree; no servo movement.");
      }
    } else {
      // Condition not met: neither category has 3 samples greater than 5
      print("Cycle " + cycleCount + ": Trigger condition not met (not all three samples > 5).");
    }
  } else {
    // Not enough samples yet for a valid look-back.
    print("Cycle " + cycleCount + ": Awaiting more samples for decision.");
  }
  
  cycleCount++;
  sleep(100); // Adjust overall cycle delay as needed.
}

Explanation

  1. Data Acquisition & servo Jumps:
    The loop reads a negIR sample, moves the servo by an increment (3 degrees) to get a posIR sample, and then moves it back. This simulates your process of taking alternating samples while panning the sensor.

  2. Storing Samples:
    Two arrays-posIRSamples and negIRSamples-store the most recent three readings each. The arrays are trimmed to always hold just the last three samples using shift().

  3. Triggering a Decision:
    Once three samples exist, the code checks that all three values in one category are greater than 5. This acts as the "trigger."

    • If either posIR or negIR meets this, the script performs a sample-by-sample comparison.
  4. Comparison for servo Movement:
    For each of the three sample pairs, we check if the triggered category’s sample is consistently higher (or lower) than the other category’s sample.
    If all three comparisons show posIR > negIR, the servo is moved 1 right (i.e. its position decreased by 1).
    If all three show negIR > posIR, the servo is moved 1 left (i.e. its position increased by 1).
    If they do not agree, no movement is made.

  5. Cycle Delay & Debug Messages:
    The sleep() calls and printed messages help regulate sensor reading timing and make debugging easier.

Feel free to adjust port numbers, sleep durations, and debug messages to suit your robot’s configuration. If you have any more questions or need further adjustments, let me know. Happy coding!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#16  

Thank you @ Athena!  You have provided lots of information for me to process.  The code runs great and gives me the data I desired. Jack