Load waves from my csv file

Hi, I am trying to load the wave header to get the Start and Increment of my csv file.


    // Here is the beginning of an example file:
    // X,                 CH2,        CH3,                     Start,      Increment
    // Sequence,   VOLT,        VOLT,                   -6.00E    -6,1.00E-8
    // 0,                -8.00E      -2,8.00E-2
    // 1,                8.00E-2,    0.00E+0
    // 2,                -8.00E-2,    0.00E+0
    // 3,                 8.00E-2,     0.00E+0
    // 4,                -8.00E-2,      0.00E+0

I am trying to use 

LoadWave/D/J/O/L={0,1,0,3,0}/A=dummy /p=my_path fileName

if the number of channels change then my L ={} will change correspondingly

I think but there should be a  way for all possible # of channels. So is there a way to load the waves and get the number of columns first, lets call that ncolums, then L will be L={0,1,0,ncolums-2,0}, so that I could always load the wave header of last two columns of my data. Thanks 

 

 

I have cleaned up (I think) the data you provided. I think "-6,1.00E-8" should be "1.00E-8".

Also "-6.00E" should be "-6.00E+0" and there was an extraneous "2," in the first line of data.

This gives the following:

X,    CH2,    CH3,    Start,    Increment
Sequence,   VOLT,        VOLT,    -6.00E+0,    1.00E-8
0,    -8.00E,    8.00E-2
1,    8.00E-2,    0.00E+0
2,    -8.00E-2,    0.00E+0
3,    8.00E-2,    0.00E+0
4,    -8.00E-2,    0.00E+0

Given that data, the following code determines the number of data columns to be loaded and loads them:

Function FindNumberOfDataColumns(pathName, fileName)
    String pathName     // Name of an Igor symbolic path or ""
    String fileName     // Name of file or full path to file
   
    Variable refNum

    Open/R/P=$pathName refNum as fileName
   
    String buffer, text
    Variable line = 0
   
    do
        FReadLine refNum, buffer
        if (strlen(buffer) == 0)
            Close refNum
            return -1                       // Error: No more data
        endif
        text = buffer[0,7]
        if (CmpStr(text,"Sequence") == 0)
            // This line tells us the number of columns
            int numberOfColumns = ItemsInList(buffer,",")
            int numberOfDataColumns = numberOfColumns - 2   // We want to skip the last two columns
            Close refNum
            return numberOfDataColumns
        endif
        line += 1
    while(1)
   
    return -1       // We will never get here
End

Function FindNumberOfDataColumnsAndLoad(pathName, fileName)
    String pathName     // Name of an Igor symbolic path or "" for dialog
    String fileName     // Name of file or full path to file or "" for dialog

    // First get a valid reference to a file
    if ((strlen(pathName)==0) || (strlen(fileName)==0))
        // Display dialog looking for file.
        Variable refNum
        Open/D/R/P=$pathName refNum as fileName
        fileName = S_fileName           // S_fileName is set by Open/D
        if (strlen(fileName) == 0)      // User cancelled?
            return -2
        endif
    endif

    // Now find the number of data columns in the file
    Variable numberOfDataColumns = FindNumberOfDataColumns(pathName, fileName)
    if (numberOfDataColumns <= 0)
        Print "FindNumberOfDataColumnsAndLoad Error - Could not find number of data columns"
        return -1
    endif
   
    // Now load the data
    int firstDataLine = 2
    LoadWave/Q/J/D/P=$pathName/E=1/K=0/L={0,firstDataLine,0,0,numberOfDataColumns}/N=temp fileName
       
    return 0
End

There are some improvements that I would do:

1. Skip the first column which is of no use assuming that it is always a sequence starting from 0 and stepping by 1.

2. Provide better wave names using the /B flag.

3. Assuming that Start and Increment refer to the starting X value and delta X value, use these to set the X scaling of the loaded waves.

I can help you with those tasks but I'd prefer to work on an actual data file which you can upload as an attachment.

Yes, what I am trying to do is to write a Macro that could load the waves. And the first thing to do is get the starting X value and delta X value, so I could use these to set the X scaling of the loaded waves. But I would like to write a macro that could work for all possible # of channels instead of only two. That is why I am asking how to get the header of last two columns. Thanks

The code above does load all columns of data regardless of how many there are.

Here is an updated version that:

1. Skips the first column which is of no use assuming that it is always a sequence starting from 0 and stepping by 1.

2. Names the output waves CH0, CH1,...

3. Sets the X scaling of each output wave based on the last two numbers in the "Sequence" line.

This code works assuming that the format of the text in the file matches the format of my cleaned-up text in the post above.

If you attach an actual data file, I will test to make sure this code works with your actual data file.

Function ExtractScaling(buffer, x0, dx)
    String buffer       // e.g., "Sequence, VOLT, VOLT, -6.00E, 1.00E-8"
    double& x0          // Output
    double& dx          // Output

    x0 = 123
    dx = 456
   
    String list = ReplaceString(" ", buffer, "")        // Remove spaces
    int numItems = ItemsInList(list, ",")
    String x0Str = StringFromList(numItems-2, list, ",")
    x0 = str2num(x0Str)
    String dxStr = StringFromList(numItems-1, list, ",")
    dx = str2num(dxStr)
   
    String expr = ""
   
    return 0   
End

Function SetWaveScaling(listOfWaveNames, x0, dx)
    String listOfWaveNames
    double x0, dx
   
    int numItems = ItemsInList(listOfWaveNames)
    int itemNumber
    for(itemNumber=0; itemNumber<numItems; itemNumber+=1)
        String name = StringFromList(itemNumber, listOfWaveNames)
        WAVE w = $name
        SetScale/P x, x0, dx, w
    endfor
End

Function FindNumberOfDataColumnsAndScaling(pathName, fileName, x0, dx)
    String pathName     // Name of an Igor symbolic path or ""
    String fileName     // Name of file or full path to file
    double& x0          // Output
    double& dx          // Output
   
    Variable refNum

    Open/R/P=$pathName refNum as fileName
   
    String buffer, text
    Variable line = 0
   
    do
        FReadLine refNum, buffer
        if (strlen(buffer) == 0)
            Close refNum
            return -1                       // Error: No more data
        endif
        text = buffer[0,7]
        if (CmpStr(text,"Sequence") == 0)
            // This line tells us the number of columns
            int numberOfColumns = ItemsInList(buffer,",")
            int numberOfDataColumns = numberOfColumns - 2   // We want to skip the last two columns
            Close refNum
            ExtractScaling(buffer, x0, dx)
            return numberOfDataColumns
        endif
        line += 1
    while(1)
   
    return -1       // We will never get here
End

Function FindNumberOfDataColumnsAndLoad(pathName, fileName)
    String pathName     // Name of an Igor symbolic path or "" for dialog
    String fileName     // Name of file or full path to file or "" for dialog

    // First get a valid reference to a file
    if ((strlen(pathName)==0) || (strlen(fileName)==0))
        // Display dialog looking for file.
        Variable refNum
        Open/D/R/P=$pathName refNum as fileName
        fileName = S_fileName           // S_fileName is set by Open/D
        if (strlen(fileName) == 0)      // User cancelled?
            return -2
        endif
    endif

    // Now find the number of data columns in the file
    double x0, dx
    Variable numberOfDataColumns = FindNumberOfDataColumnsAndScaling(pathName, fileName, x0, dx)
    if (numberOfDataColumns <= 0)
        Print "FindNumberOfDataColumnsAndLoad Error - Could not find number of data columns"
        return -1
    endif
   
    // Now load the data
    int firstDataLine = 2
    int firstColumnToLoad = 1       // Skip column 0 which is just 0,1,2,...
    LoadWave/Q/J/D/P=$pathName/K=0/O/A=CH/E=1/L={0,firstDataLine,0,firstColumnToLoad,numberOfDataColumns}/N=temp fileName
   
    SetWaveScaling(S_waveNames, x0, dx)
   
    return 0
End

 

Much appreciated!

Here is the Macro that I have so far that only works for two channels, could you try to modify that, so it could work for al possible number of channels? I am hesitating to give you my actual data, since it is important.

Macro loadtrace(tracenum,filename,pathname)
//7/22/2024 This macro loads a Rigol scope trace.
    variable/d tracenum // sets naming of waves
    String fileName // name of file to load or "" to get dialog
    String Pathname // name of path or "" get dialog
    variable/d num_waves_loaded
    String listItem
    variable/d start,increment
    String startwavename, incrementwavename
    // Silent 1; PauseUpdate
    String w0, w1, w2, w3, w4 //  Wavenames of loaded waves.
    // can have up to 4 traces, and time is first column
    String newname0,newname1,newname2,newname3,newname4 // New wave names
    // Here is the beginning of an example Rigol file:
    // X,CH2,CH3,Start,Increment
    // Sequence,VOLT,VOLT,-6.00E-6,1.00E-8
    // 0,-8.00E-2,8.00E-2
    // 1,8.00E-2,0.00E+0
    // 2,-8.00E-2,0.00E+0
    // 3,8.00E-2,0.00E+0
    // 4,-8.00E-2,0.00E+0
    // First we have to load the wave header to get the time scale since that is how Rigol formats their csv files:
    //LoadWave/D/J/O/L={0,1,0,3,0}/A=dummy /p=$Pathname fileName
    LoadWave/D/J/O/L={0,1,0,3,0}/A=dummy /p=my_path fileName
    // /L={nameLine, firstLine, numLines, firstColumn, numColumns}
    print("Loaded S_fileName=")
    print(S_fileName)
    print("Loaded S_fileName=")
    print(S_path)
    print(S_waveNames)
    startwavename=StringFromList(0, S_waveNames)
    incrementwavename=StringFromList(1, S_waveNames)
    Pathname=S_path // for next load command, if user selected using dialog
    fileName=S_fileName// for next load command
    // start = ....
    start=$startwavename[0]
    // increment = ...
    increment=$incrementwavename[0]
    print("start=")
    print(start)
    print("increment=")
    print(increment)
    killwaves $startwavename
    killwaves $incrementwavename
    // Next, load in waves.
    print ("loading filanemame=")
    print filename
    //LoadWave/A=dummy/Q/D/G/O/p=my_path filename
    LoadWave/D/J/O/L={0,2,0,0,3}/W/A/Q /p=my_path fileName
    if (V_flag==0) // Now waves loaded. Perhaps user cancelled.
        return
    endif      
    print("Loaded S_fileName=")
    print(S_fileName)
    print("Loaded S_fileName=")
    print(S_path)
    print("S_waveNames=")
    print(S_waveNames) 
    //Print ItemsInList(stringList, ",")
    print("num_waves_loaded=V_flag=")
    print(V_flag)
    print("done printing....")
    w0= StringFromList(0,S_waveNames) // this is the point number, we don't want it
    w1= StringFromList(1,S_waveNames)
    w2= StringFromList(2,S_waveNames)
    w3= StringFromList(3,S_waveNames)
    w4= StringFromList(4,S_waveNames)
    // put the name of waves into string variables
    if (strlen(w0) != 0) // w0 exists
        print("w0 ok!")
        w0= StringFromList(0,S_waveNames)
        killwaves $w0 // this is the point number, we don't want it
    endif
    if (strlen(w1) != 0) // w1 exists
        print("w1 ok!")
        w1= StringFromList(1,S_waveNames)
        newname1=w1+"_" + num2str(tracenum)
        //newname1="data_1_" + num2str(tracenum)
        duplicate/o $w1, $newname1
        killwaves $w1
        // set scale to volts      
        SetScale/P x start,increment,"s", $newname1
        SetScale d 0,0,"V", $newname1
    endif
    if (strlen(w2) != 0) // w2 exists
        print("w2 ok!")
        w2= StringFromList(2,S_waveNames)
        newname2=w2+"_" + num2str(tracenum)
        //newname2="data_2_" + num2str(tracenum)
        duplicate/o $w2, $newname2
        killwaves $w2
        // set scale to volts      
        SetScale/P x start,increment,"s", $newname2
        SetScale d 0,0,"V", $newname2
    endif
    if (strlen(w3) != 0) // w3 exists
        print("w3 ok!")
        w3= StringFromList(3,S_waveNames)
        newname3=w3+"_" + num2str(tracenum)
        //newname3="data_3_" + num2str(tracenum)
        duplicate/o $w3, $newname3
        killwaves $w3
        // set scale to volts
        SetScale/P x start,increment,"s", $newname3
        SetScale d 0,0,"V", $newname3
    endif
    if (strlen(w4) != 0) // w4 exists
        print("w4 ok!")
        w4= StringFromList(4,S_waveNames)
        //newname4="data_4_" + num2str(tracenum)
        newname4=w4+"_" + num2str(tracenum)
        duplicate/o $w4, $newname4
        killwaves $w4
        SetScale/P x start,increment,"s", $newname4
        SetScale d 0,0,"V", $newname4
        // set scale to volts      
    endif
EndMacro

 

I wonder, did you look at what hrodstein has provided? Did it not work for you? It seems a very neat solution to me... Also, macros are very old tech and should be avoided in favor of functions.

Anyway, here is a code fragment which wraps your manual assignment into a simple loop. Things could be done much more neatly (see Howards solution), but if you just want to quickly modify what you already have...

... code until the main load command ...

    LoadWave/D/J/O/L={0,2,0,0,3}/W/A/Q /p=my_path fileName
    if (V_flag==0)
        return
    endif
    Variable i
    for (i=1; i<ItemsInList(S_waveNames);i+=1)
        string currW = StringFromList(i,S_waveNames)
        string Wname = currW + "_" + num2str(tracenum)
        Duplicate/o $currW, $Wname
        KillWaves $currW
        SetScale/P x start,increment,"s", $Wname
        SetScale d 0,0,"V", $Wname
    endfor

... whatever else you want to do ...

 

In reply to by minghaoj

 

minghaoj wrote:

 

   LoadWave/D/J/O/L={0,2,0,0,3}/W/A/Q /p=my_path fileName


Agree to chozo's take. It really comes down to the /L={0,2,0,0,3}. The 3 dictates how many columns you are loading (in your case the X, CH2, and CH3). You can increase this number accordingly if you have additional channels of data. Since your meta data (Start and Increment) are in the first rows, you won't have issue loading your data.

I am guessing your question is how to have your function be versatile to handle things beyond (the repetitive chunks of code for) w0 to w4??

Personally, for data like this, I prefer to load them into a 2D wave matrix by adding /M to LoadWave and go from there.

Another option would be creating a function that does your post loading processing and then feed the waves generated to that function in a loop. I see you already found the list of names of waves are stored in S_waveNames. You can use ItemsInList to determine the number of channels/waves loaded. Then use a For loop to repeat the  process.

int i, numWaves
string wvName, newWvName
numWaves = itemsinlist(S_waveNames)
for(i =0; i< numWaves;i++)
    wvName= StringFromList(i,S_waveNames)
    newWvName = wvName + "_" + num2str(tracenum)
    duplicate/o $wvName, $newWvName
    killwaves $wvName
    // set scale to volts
    SetScale/P x start,increment,"s", $newWvName
    SetScale d 0,0,"V", $newWvName
endfor

Personally, I would change the order of operation as follow and use name the waves differently (you can also use the LoadWave/Name added in Igor Pro 9 or later to handle your wave name changing. Note to myself, start using this).

int i, numWaves
string loadedWvName, wvName
numWaves = itemsinlist(S_waveNames)
for(i =0; i< numWaves;i++)
    loadedWvName= StringFromList(i,S_waveNames)
    // set scale to volts
    SetScale/P x start,increment,"s", $loadedWvName
    SetScale d 0,0,"V", $loadedWvName
    //Chose one below
    //If you prefer the same channel of data from different files are grouped together in Data Browser use this line
    wvName = "CH" + num2str(i+1) + "_" + num2str(tracenum)
    //If you prefer data of different channels from the same data file are grouped together in Data Browser use this line.
    wvName = "_" + num2str(tracenum) + "_CH" + num2str(i+1) //Add "_" in the front as you can't start a wave name using a number, unless you are doing liberal name. The next line of code will fix this for you anyways though.
    //It is always important to verify your wave name is valid.
    //Alternatively, you can use CreateDataObjectName for more options only if you are using Igor Pro 9.0 or later.
    wvName = CleanupName(wvName, 0)
    duplicate/o $loadedWvName, $wvName
    killwaves $loadedWvName
endfor

Unrelated, yet related. I have created loaders for several different types of data files with meta data in it. The solution is similar to what hrodstein provided here. It involves a combination of FReadline and LoadWave (sometimes multiple of each).

Please correct me if I am wrong about this. One pet peeve of mine is that LoadWave/L seems to be limited by the number of columns in the firstLine of data which overrides the numColumns parameter if the later is larger. I am sure there are underlying reason why it does this, but part of me hope we can have an option to override that.

The situation is the worst when multiple chunks of data are stored in the same file with their individual meta data. Doing multiple loops of LoadWave/L can be very slow. FReadline appears to be faster (as the file is only opened once) but requires additional post loading wave handling. Some times I can try loading the entire file into one wave and separate each chunk out. It gets messy if the chunk sizes are dynamic.

One pet peeve of mine is that LoadWave/L seems to be limited by the number of columns in the firstLine of data which overrides the numColumns parameter if the later is larger.

That's correct. It is a limitation that I have run into myself on occasion.

I am sure there are underlying reason why it does this

The underlying reason is that I didn't think of this situation when I wrote this code (about 35 years ago ;)

but part of me hope we can have an option to override that.

I think a flag that says "don't automically determine the number of columns in the file from the first line of data, follow /L instead" is doable.

 

In reply to by hrodstein

hrodstein wrote:

I think a flag that says "don't automically determine the number of columns in the file from the first line of data, follow /L instead" is doable.

 

That will be cool. I am looking forward to that feature in future updates.