.. _classes-label: Classes ******* SAS is a pure python package containing specialized data containers as well as grahical tools to ease the visualization of Sams data. .. _samslog-label: .. py:currentmodule:: samslog SamsLog ======= from sas import samslog The SamsLog() class provides a data container capable of consolidating a number of logs file of the same type into one structure. .. py:class:: SamsLog(filenames, suffix=None, verbose=False) The input variable ``filenames`` must be a list of valid log file names and/or a list of valid day names. If the names are not appended with a suffix, then the suffix parameter will be used. If the first filename has a suffix and no suffix parameter is given, then the first suffix will be used for the other files. Valid strings for the suffix are ``deg``, ``ebi``, ``gap``, ``sen``, ``sum``, ``tsn``, and ``ttp``. c.f. :ref:`file_types-label`. Since the Sams log files contain only four hours of data you are allowed to use a syntax like, :: allday_shear = SamsLog(['sams20170813'], suffix='sen') where ``sams20170813`` will be expanded to a list with the six files for that day. The suffix ``.sen`` will be appended. For example, :: allday_shear.files() ['sams201708130000.sen', 'sams201708130400.sen', 'sams201708130800.sen', 'sams201708131200.sen', 'sams201708131600.sen', 'sams201708132000.sen'] You may also pass in a list of files to read from, :: allday_shear = SamsLog(['sams201708130000.sen','sams201708130400.sen', 'sams201708130800.sen','sams201708131200.sen', 'sams201708131600.sen','sams201708132000.sen']) Or you may do a combination of both, :: allday_shear = SamsLog(['sams201708131600.sen','sams201708132000.sen', 'sams20170814', 'sams201708150000.sen','sams201708150400.sen']) Note that the class will pick up the ``.sen`` suffix from the first file name and will apply it to any file name that doesn't have a suffix yet. Also be aware that the data files are read into the data container in the order in which they are lists. No attempt is made to sort the resulting data by time stamp. Methods ------- Once the filename(s) are parsed and the files are open, the class then reads the file(s), works out whether it has sensor data (480 column), segment date (273 columns), or summary data (9 columns). It can then work out the proper address scheme from segment or segment:sensor to a data column. .. py:classmethod:: append(filenames, suffix) Like the class constructor filenames should be a list of file you want to append to the existing data. Again, these data are not sorted by date/time. The input variable, suffix, has the same connotation as in the class initialization. .. py:classmethod:: data(segsens) This method returns two or more lists; the first list contains the time stamps in Unix time format; the remaining lists depend on whether a segment number of a segment-sensor number was sent as the input argument :ref:`numbering-label`. If only a segment number is passed in, then all the data relevant to that segment will be returned. For ``.sen`` files, this would be six lists of sensor data. For ``.ttp`` files, this would be 3 lists containing the tip/tilt/piston values for the segment. If a segment-sensor value is passed in, then any sensor related file will return a single column with just that sensor's values. For example, :: shear = SamsLog('sams201708130000.sen') tstamp, shear23_2 = shear.data('M23:2') gap = SamsLog('sams201708130000.gap') tstamp, gap23_1, gap23_2, gap23_3, gap23_4, gap23_5, gap23_6 = gap.data('M23") Note that for any edge segments, if you ask for all the sensors or a non-existant segment-sensor combinartion then ``None`` will be returned for the non-existant sensors. It is not an error to pass a segment-sensor value as an input to a ``.ttp`` data set, but the sensor value will meaningless. The return values are of type and are one-dimensional row vectors. .. py:classmethod:: starttime() .. py:classmethod:: endtime() The class also has instance variables with the start and end time of the file(s) that have been read in. These may be accessed though the methods as follows :: gap.starttime() 1502582400.0 .. py:classmethod:: files() This method returns the complete list of files that have been read into the data set. Thus it represents the expansion of the file list passed in during intialization and append functions. .. py:classmethod:: suffix() Return the suffix or type of file with out the dot. :: gap = SamsLog([sams20170813', suffix='gap') gap.suffix() 'gap' .. py:classmethod:: size() Returns the size of the data contains as the value ``rows:columns``. :: gap.size() '32614:481' .. py:currentmodule:: segments .. _samscmdlog-label: SamsCmdLog ========== from sas import samcmdlog The SamsCmdLog() class is very similar to the SamsLog() class in that it provides a data container for the ``log`` files. These files list the commands received by the Sams server. .. py:class:: SamsCmdLog(filenames, suffix='log', verbose=False) The input variable ``filenames`` must be a list of valid log file names and/or a list of valid day names. If the names are not appended with a suffix, then the suffix parameter ``log`` will be used. The ``suffix`` argument is still in the calling pattern for the class but it is ignored. However, the class will generate an error if the ``suffix`` argument is passed in with anything other than ``'log'`` as the argument. This is done only for consistency with the class :ref:`samslog-label`. The usage of the ``filenames`` variable is the same as in the :ref:`samslog-label` class. You may use an abbreviated file name to include all the files for a full day. Or you may use the explicit list of files, with or with out the suffix. allcmds = SamsCmdLog(['sams20170813']) morning_cmds = SamsCmdLog(['sams201708130000', 'sams201708130400', 'sams201708130800.log']) Methods ------- Once the files are read into the container, various methods are available to work with the data .. py:classmethod:: append(filenames, suffix='log') Like the class constructor filenames should be a list of file you want to append to the existing data. Again, these data are not sorted by date/time. The input variable, suffix, has the same connotation as in the class initialization. .. py:classmethod:: find_command(command=None, starttime=None, endtime=None) Return all instances of command found between starttime and endtime. An instance is a tuple of the form (time, commandStr). If starttime/endtime are None then the entire list is searched otherwise only the tuples found between startime and endtime are returned. If command is None, then all commands between starttime/endtime are returned. If all three are None, then the entire list is returned. :: allcmds.find_command('installsen') [(15345678901, 'installsen 85 3'), (12456789012, 'installsen 32 1')] .. py:classmethod:: starttime() .. py:classmethod:: endtime() The class also has instance variables with the start and end time of the file(s) that have been read in. These may be accessed though the methods as follows :: allcmds.starttime() 1502582400.0 .. py:classmethod:: files() This method returns the complete list of files that have been read into the data set. Thus it represents the expansion of the file list passed in during intialization and append functions. .. py:classmethod:: suffix() Return the suffix or type of file with out the dot. In this particular class the suffix will always be `log` :: cmds = SamsCmdLog([sams20170813']) cmds.suffix() 'log' .. py:classmethod:: size() Returns the number of entries in the data container. :: cmds.size() 3261 .. _segments-label: segments.py =========== from sas import segments The file segments.py contains the conversion tables between segment number schemes. These conversion tables are simply dictionaries with keys in the relevant mirror numbering scheme, c.f. :ref:`numbering-label`. .. py:data:: Mn2Sn .. py:data:: Sn2Mn Convert to/from the Mirror number of Frank Ray to the Sams number scheme from Blueline Engineering. :: Mn2Sn['M02'] 'S78' Sn2Mn['S78'] 'M02' .. py:data:: CR2Sn .. py:data:: Sn2CR Convert to/from the Column:Row numbering scheme of Phillip McQueen and the Sams number scheme from Blueline Engineering. :: CR2Sn['7:01'] 'S78' Cn2CR['S78'] '7:01' .. py:data:: Mn2CR .. py:data:: CR2Mn Convert to/from the Mirror number of Frank Ray to the Column:Row of Phillip McQueen. :: Mn2CR['M02'] '7:01' CR2Mn['7:01'] 'M02' .. py:data:: m_re .. py:data:: s_re .. py:data:: cr_re This are regular expession object as described in the regular expression package re. They are build with ``re.compile()``, c.f. `re `_ for more information. There is one each for the Mnumbers, the Snumbers, and the CRnumbers. The regular expression used to create the objects are * M_re = r'^(M(\\d\\d))(-(\\d))?\\Z' * S_re = r'^(S(\\d\\d))(-(\\d))?\\Z' * CR_re = r'^((\\d{1,2}):(\\d{1,2}))(-(\\d))?\\Z' .. py:data:: SnSen_column This array converts from segment-sensor number in the Sams numbering scheme to column number in the data array for 'sen', 'gap', 'tsn', or 'deg' files. It may also be used to see it a sensor exists by testing for a Snum-sen key value and seeing if you get a return value. .. py:function:: get_seg_sen_type(segsen) Parse a segment-sensor string and return the type of numbering system used. The functions returns a tuple of five values; * segment name in string form, i.e. M54 or S23 * segment number or the column number if this is a CRnum as an integer * row number as an integer of this is a CRnum, otherwise None * sensor number if provided, otherwise None * type of number as 'Mnum', 'Snum', 'CRNum', or None For example: :: get_seg_sen_type('M43-2') ('M43', 43, None, 2, 'Mnum') A CRnum may have leading zero in either the columm or the row but the number may only be two digits. The returned string however, will not have any leading zeros and may be used to index into the CR2Sn or CR2Mn arrays. :: get_seg_sen_type('05:04-6') ('5:4', 5, 4, 6, 'CRnum') get_seg_sen_type('05:04') ('5:4', 5, 4, None, 'CRnum') .. py:function:: get_sams_num(segsen) .. py:function:: get_mir_num(segsen) These two functions return the S number or the M number given any valid numbering scheme as input. Two values are returned, the S or M number as a string and the S or M number as an integer:: get_sams_num('M42') ('S11', 11) get_sams_num('2:4-6') ('S44', 44) get_mir_num('S11') ('M42', 42') get_mir_num('5:4') ('M26', 26) .. py:data:: row_cols = [7, 8, 9, 10, 11, 12, 11, 10, 9, 8, 7] A list with the number of rows in each column, 1-11. This list is useful if you wish to do looping over the column:row order, e.g. :: for c = range(0,11): row = row_cols[c-1] for r in range(0, row): .. py:function:: gen_col_row() Generate a list of the column:row strings for the segments in the array. This is useful for running loops in column:row index and is used extensively in the plotting routines, e.g. :: for cr in gen_col_row(): m_num, empty = get_mir_num(cr) x, y = plot.segment_position[cr] plot_something_clever(m_num, x, y) .. py:currentmodule:: plot .. _plot-label: plot.py ======= from sas import plot The file plot.py contains some useful plotting routines. Plotting of the full array or a single segment is based on a unit sized hexagon where the distance from the edge to edge is set to one unit. As we work out other items that need to be be routinely plotted, they can be added to this file. Classes ------- .. py:class:: Hexagon(size = 1.0, offset=(0.0, 0.0)) The unit hexagon. The create vertices for a hexagon with a 1.0 meter distance from edge to edge. Note that this means that the vertices fall outside the unit cell so watch out for overlap when plotting. The offset (x, y) are added to each vertex and will offset the hexagon from a (0, 0) center position. Useful for plotting routines that want to draw a hexagon. .. py:attribute:: x .. py:attribute:: y These are the lists of the x, y position of the vertices. They can be access as :: h = Hexagon() plt.plot(h.x, h.y) Note that this syntax may change. I would like to make Hexagon a derived class from a parent class Polygon but haven't figured out how to make things work yet. .. py:method:: vertices() Returns a list of tuples of the (x, y) positions of the vertices. :: h = Hexagon() h.vertices() [(,) ] Functions --------- .. py:function:: make_seg_pos() Make a dictionary with CRnums as keys and values as a tuple of the (x, y) position of the segment in the array based on a unit hexagon. This means the plot will be 11.0 units high and 11.15 units wide. .. py:data:: segment_position A dictionary with keys of CRnum and values of (x, y) for the segment positions based on a unit hexagon. This dictionary was initially create with ``make_seg_pos()``. This is useful in loops like :: from sas import segments as sg from sas import plot as sp for cr in sg.gen_col_row(): x, y = sp.segment_position[cr] plot_something_clever(x, y) .. py:function:: plot_array(ax, color='grey') Plots 91 unit hexagons in their proper positions. See :ref:`segment locations ` for an example. ``ax`` is a reference to a ``matplotlib.axes`` instance. ``color`` is any valid matplotlib color, e.g. :: fig = plt.figure(figsize=(7,7)) ax = fig.add_subplot(111) plot_array(ax, color='red') .. py:function:: plot_ttp_summary(ttplog) Plot the Tilt/Tilt vectors agains an array of segments .. py:function:: plot_sum_summary(sumlog) Plot the data from a summary files. Three plots are created; the sensor error as RSE and TRSE; the gap and groc; and the average sensor temperature. More functions as we think of them.